1.7.0 // Renovation. Merge Gitee PR!40 from upd/1.7.0
This commit is contained in:
commit
b0650de948
|
@ -29,7 +29,7 @@
|
|||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.</string>
|
||||
<string>© 2021-2022 vChewing Project.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "vChewing Installer";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
CFEULAContent = "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n2. No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements above.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n";
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Derived from OpenVanilla McBopopmofo Project."; ObjectID = "QYf-Nf-hoi"; */
|
||||
"QYf-Nf-hoi.title" = "Derived from OpenVanilla McBopopmofo Project.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine.";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Placeholder for showing copyright information."; ObjectID = "eo3-TK-0rB"; */
|
||||
// "eo3-TK-0rB.title" = "Placeholder for showing copyright information.";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "威注音入力 実装用アプリ";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
CFEULAContent = "以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じことを許可する権利も無制限に含まれます。\n\nイ)上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとします。\n\nロ)上記の通知要件を満たすために必要な場合を除き、コントリビューターの商号、商標、サービスマーク、または製品名を使用するための商標ライセンスは付与されていません。\n\nソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証もなく提供されます。ここでいう保証とは、商品性、特定の目的への適合性、および権利非侵害についての保証も含みますが、それに限定されるものではありません。\n作者または著作権者は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切の請求、損害、その他の義務について何らの責任も負わないものとします。";
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Derived from OpenVanilla McBopopmofo Project."; ObjectID = "QYf-Nf-hoi"; */
|
||||
"QYf-Nf-hoi.title" = "OpenVanilla 小麦注音プロジェクトから派生。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "鉄恨ボポモフォエンジン開発:Shiki Suen。\n入力状態管理システム開発:Zonble Yang。\nmacOS 版威注音の開発:Shiki Suen, Isaac Xen, Hiraku Wang, など。\n威注音語彙データの維持:Shiki Suen。\nMegrez 辞書処理エンジン:Shiki Suen(Lukhnos の Gramambular C++ エンジンを Swift で再開発したものである)。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "macOS 版威注音の開発:Shiki Suen, Isaac Xen, Hiraku Wang, など。\n威注音語彙データの維持:Shiki Suen。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Placeholder for showing copyright information."; ObjectID = "eo3-TK-0rB"; */
|
||||
"eo3-TK-0rB.title" = "Placeholder for showing copyright information.";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "威注音安装程式";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
CFEULAContent = "软件之著作权利人依此麻理授权条款,将其对于软件之著作权利授权释出,只须使用者践履以下二项麻理授权条款叙明之义务性规定,其即享有对此软件程式及其相关说明文档自由不受限制地进行利用之权利,范围包括「使用、重制、修改、合并、出版、散布、再授权、及贩售程式重制作品」等诸多方面之应用,而散布程式之人、更可将上述权利传递予其后收受程式之后手,倘若其后收受程式之人亦服膺以下二项麻理授权条款之义务性规定,则其对程式亦享有与前手运用范围相同之同一权利。\n\n甲、散布此一软件程式者,须将本条款其上之「著作权声明」及以下之「免责声明」内嵌于软件程式及其重制作品之实体之中。\n\n乙、敝授权合约不提供对「贡献者」之商品名称、商标、服务标志或产品名称之商标许可,除非用以满足履行上文所述义务之必要。\n\n因麻理软件程式之授权模式乃是无偿提供,是以在现行法律之架构下可以主张合理之免除担保责任。麻理软件之著作权人或任何之后续散布者,对于其所散布之麻理软件程式皆不负任何形式上实质上之担保责任,明示亦或隐喻、商业利用性亦或特定目之使用性,这些均不在保障之列。利用麻理软件程式之所有风险均由使用者自行担负。假如所使用之麻理程式发生缺陷性问题,使用者需自行担负修正、改正及必要之服务支出。麻理软件程式之著作权人不负任何形式上实质上之担保责任,无论任何一般之、特殊之、偶发之、因果关系式之损害,或是麻理软件程式之不适用性,均须由使用者自行负担。\n";
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Derived from OpenVanilla McBopopmofo Project."; ObjectID = "QYf-Nf-hoi"; */
|
||||
"QYf-Nf-hoi.title" = "该专案由 OpenVanilla 小麦注音专案衍生而来。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "铁恨注音并击输入处理引擎研发:Shiki Suen。\n输入法状态管理引擎研发:Zonble Yang。\n威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。\n天权星语汇引擎:Shiki Suen,用 Swift 将 Lukhnos 的 C++ Gramambular 重写而得。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Placeholder for showing copyright information."; ObjectID = "eo3-TK-0rB"; */
|
||||
// "eo3-TK-0rB.title" = "Placeholder for showing copyright information.";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
CFBundleName = "威注音安裝程式";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
CFEULAContent = "軟體之著作權利人依此麻理授權條款,將其對於軟體之著作權利授權釋出,只須使用者踐履以下二項麻理授權條款敘明之義務性規定,其即享有對此軟體程式及其相關說明文檔自由不受限制地進行利用之權利,範圍包括「使用、重製、修改、合併、出版、散布、再授權、及販售程式重製作品」等諸多方面之應用,而散布程式之人、更可將上述權利傳遞予其後收受程式之後手,倘若其後收受程式之人亦服膺以下二項麻理授權條款之義務性規定,則其對程式亦享有與前手運用範圍相同之同一權利。\n\n甲、散布此一軟體程式者,須將本條款其上之「著作權聲明」及以下之「免責聲明」內嵌於軟體程式及其重製作品之實體之中。\n\n乙、敝授權合約不提供對「貢獻者」之商品名稱、商標、服務標誌或產品名稱之商標許可,除非用以滿足履行上文所述義務之必要。\n\n因麻理軟體程式之授權模式乃是無償提供,是以在現行法律之架構下可以主張合理之免除擔保責任。麻理軟體之著作權人或任何之後續散布者,對於其所散布之麻理軟體程式皆不負任何形式上實質上之擔保責任,明示亦或隱喻、商業利用性亦或特定目之使用性,這些均不在保障之列。利用麻理軟體程式之所有風險均由使用者自行擔負。假如所使用之麻理程式發生缺陷性問題,使用者需自行擔負修正、改正及必要之服務支出。麻理軟體程式之著作權人不負任何形式上實質上之擔保責任,無論任何一般之、特殊之、偶發之、因果關係式之損害,或是麻理軟體程式之不適用性,均須由使用者自行負擔。\n";
|
||||
|
|
|
@ -56,8 +56,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "Derived from OpenVanilla McBopopmofo Project."; ObjectID = "QYf-Nf-hoi"; */
|
||||
"QYf-Nf-hoi.title" = "該專案由 OpenVanilla 小麥注音專案衍生而來。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "鐵恨注音並擊輸入處理引擎研發:Shiki Suen。\n輸入法狀態管理引擎研發:Zonble Yang。\n威注音 macOS 程式研發:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音詞庫維護:Shiki Suen。\n天權星語彙引擎:Shiki Suen,用 Swift 將 Lukhnos 的 C++ Gramambular 重寫而得。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "VW8-s5-Wpn"; */
|
||||
"VW8-s5-Wpn.title" = "威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Placeholder for showing copyright information."; ObjectID = "eo3-TK-0rB"; */
|
||||
// "eo3-TK-0rB.title" = "Placeholder for showing copyright information.";
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
vChewing macOS: MIT-NTL License 麻理(去商标)授权合约
|
||||
|
||||
© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.
|
||||
铁恨注音并击输入处理引擎研发:Shiki Suen。
|
||||
输入法状态管理引擎研发:Zonble Yang。
|
||||
© 2021-2022 vChewing Project.
|
||||
威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。
|
||||
威注音词库维护:Shiki Suen。
|
||||
天权星语汇引擎:Shiki Suen,用 Swift 将 Lukhnos 的 C++ Gramambular 重写而得。
|
||||
|
||||
软件之著作权利人依此麻理授权条款,将其对于软件之著作权利授权释出,只须使用者践履以下二项麻理授权条款叙明之义务性规定,其即享有对此软件程式及其相关说明文档自由不受限制地进行利用之权利,范围包括「使用、重制、修改、合并、出版、散布、再授权、及贩售程式重制作品」等诸多方面之应用,而散布程式之人、更可将上述权利传递予其后收受程式之后手,倘若其后收受程式之人亦服膺以下二项麻理授权条款之义务性规定,则其对程式亦享有与前手运用范围相同之同一权利。
|
||||
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
vChewing macOS: MIT-NTL License 麻理(去商標)授權合約
|
||||
|
||||
© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.
|
||||
鐵恨注音並擊輸入處理引擎研發:Shiki Suen。
|
||||
輸入法狀態管理引擎研發:Zonble Yang。
|
||||
© 2021-2022 vChewing Project.
|
||||
威注音 macOS 程式研發:Shiki Suen, Isaac Xen, Hiraku Wang, 等。
|
||||
威注音詞庫維護:Shiki Suen。
|
||||
天權星語彙引擎:Shiki Suen,用 Swift 將 Lukhnos 的 C++ Gramambular 重寫而得。
|
||||
|
||||
軟體之著作權利人依此麻理授權條款,將其對於軟體之著作權利授權釋出,只須使用者踐履以下二項麻理授權條款敘明之義務性規定,其即享有對此軟體程式及其相關說明文檔自由不受限制地進行利用之權利,範圍包括「使用、重製、修改、合併、出版、散布、再授權、及販售程式重製作品」等諸多方面之應用,而散布程式之人、更可將上述權利傳遞予其後收受程式之後手,倘若其後收受程式之人亦服膺以下二項麻理授權條款之義務性規定,則其對程式亦享有與前手運用範圍相同之同一權利。
|
||||
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
vChewing macOS: MIT商標不許可ライセンス (MIT-NTL License)
|
||||
|
||||
鉄恨ボポモフォエンジン開発:Shiki Suen。
|
||||
入力状態管理システム開発:Zonble Yang。
|
||||
© 2021-2022 vChewing Project.
|
||||
macOS 版威注音の開発:Shiki Suen, Isaac Xen, Hiraku Wang, など。
|
||||
威注音語彙データの維持:Shiki Suen。
|
||||
Megrez 辞書処理エンジン:Shiki Suen(Lukhnos の Gramambular C++ エンジンを Swift で再開発したものである)。
|
||||
|
||||
以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じことを許可する権利も無制限に含まれます。
|
||||
|
||||
|
|
|
@ -2,12 +2,9 @@ DISCLAIMER: The vChewing project, having no relationship of cooperation or affil
|
|||
|
||||
vChewing macOS: MIT-NTL License
|
||||
|
||||
© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.
|
||||
Tekkon Syllable Composition Engine by Shiki Suen.
|
||||
Input State Management Architecture by Zonble Yang.
|
||||
© 2021-2022 vChewing Project.
|
||||
vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.
|
||||
vChewing Phrase Database Maintained by Shiki Suen.
|
||||
Megrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
語言:[简体中文](./README.md) | *繁體中文*
|
||||
|
||||
---
|
||||
|
||||
因不可控原因,該倉庫只能保證在 Gitee 有最新的內容可用:
|
||||
|
||||
- 下載:https://gitee.com/vchewing/vChewing-macOS/releases
|
||||
- 程式碼倉庫:https://gitee.com/vchewing/vChewing-macOS
|
||||
|
||||
# vChewing 威注音輸入法
|
||||
|
||||
威注音輸入法基於小麥注音二次開發,是**原生簡體中文、原生繁體中文注音輸入法**:
|
||||
|
||||
- 威注音是業界現階段支援注音排列種類數量與輸入用拼音種類數量最多的注音輸入法。
|
||||
- 受威注音自家的鐵恨注音並擊引擎加持。
|
||||
- 威注音的原廠詞庫內不存在任何可以妨礙該輸入法在世界上任何地方傳播的內容。
|
||||
- 相比中州韻(鼠須管)而言,威注音能夠做到真正的大千聲韻並擊。
|
||||
- 擁有拼音並擊模式,不懂注音的人群也可以受益於該輸入法所帶來的穩定的平均輸入速度。
|
||||
- 相比小鶴雙拼等雙拼方案而言,威注音雙手聲韻分工明確、且重碼率只有雙拼的五分之一。
|
||||
- 威注音對陸規審音完全相容:不熟悉台澎金馬審音的大陸用戶不會遇到與漢字讀音有關的不便。
|
||||
- 反之亦然。
|
||||
|
||||
>威注音有很多特色功能。在此僅列舉部分:
|
||||
>- 支援 macOS 螢幕模擬鍵盤(僅傳統大千與傳統倚天佈局)。
|
||||
>- 可以將自己打的繁體中文自動轉成日本 JIS 新字體來輸出(包括基礎的字詞轉換)、也可以轉成康熙繁體來輸出。
|
||||
>- 簡繁體中文語料庫彼此分離,徹底杜絕任何繁簡轉換過程可能造成的失誤。
|
||||
>- 支援近年的全字庫漢字輸入。
|
||||
>- 可以自動整理使用者語彙檔案格式、自訂聯想詞。
|
||||
>- ……
|
||||
|
||||
威注音分支專案及威注音詞庫由孫志貴(Shiki Suen)維護。小麥注音官方原始倉庫內的詞庫的內容均與孫志貴無關。
|
||||
|
||||
## 系統需求
|
||||
|
||||
建置用系統需求:至少 macOS 10.15 Catalina & Xcode 12。// 原因:Swift 封包管理支援所需。
|
||||
|
||||
編譯出的成品對應系統需求:
|
||||
|
||||
- 至少 macOS El Capitan 10.11.5,否則無法處理 Unicode 8.0 的漢字。即便如此,仍需手動升級蘋方至至少 macOS 10.12 開始隨贈的版本、以支援 Unicode 8.0 的通用規範漢字表用字(全字庫沒有「𫫇」字)。
|
||||
|
||||
- 保留該系統支援的原因:非 Unibody 體型的 MacBook Pro 支援的最後一版 macOS 就是 El Capitan。
|
||||
|
||||
- **推薦最低系統版本**:macOS 10.12 Sierra,對 Unicode 8.0 開始的《通用規範漢字表》漢字有原生的蘋方支援。
|
||||
|
||||
- 同時建議**系統記憶體應至少 4GB**。威注音輸入法佔用記憶體約 115MB 左右(簡繁雙模式)、75MB左右(單模式),供參考。
|
||||
|
||||
- 請務必使用 SSD 硬碟,否則可能會影響每次開機之後輸入法首次載入的速度。從 10.10 Yosemite 開始,macOS 就已經是針對機械硬碟負優化的作業系統了。
|
||||
|
||||
- 注:能裝 macOS 10.13 High Sierra 就不要去碰 macOS 10.12 Sierra 這個半成品。
|
||||
|
||||
- 關於全字庫支援,因下述事實而在理論上很難做到最完美:
|
||||
|
||||
- 很可惜 GB18030-2005 並沒有官方提供的逐字讀音對照表,所以目前才用了全字庫。然而全字庫並不等於完美。
|
||||
|
||||
- 有條件者可以安裝全字庫字型與花園明朝,否則全字庫等高萬國碼碼位漢字恐無法在輸入法的選字窗內完整顯示。
|
||||
|
||||
- 全字庫漢字顯示支援會受到具體系統版本對萬國碼版本的支援的限制。
|
||||
|
||||
- 有些全字庫漢字一開始會依賴萬國碼的私人造字區,且在之後被新版本萬國碼所支援。
|
||||
|
||||
## 建置流程
|
||||
|
||||
安裝 Xcode 之後,請先配置 Xcode 允許其直接構建在專案所在的資料夾下的 build 資料夾內。步驟:
|
||||
```
|
||||
「Xcode」->「Preferences...」->「Locations」;
|
||||
「File」->「Project/WorkspaceSettings...」->「Advanced」;
|
||||
選「Custom」->「Relative to Workspace」即可。不選的話,make 的過程會出錯。
|
||||
```
|
||||
在終端機內定位到威注音的克隆本地專案的本地倉庫的目錄之後,執行 `make update` 以獲取最新詞庫。
|
||||
|
||||
接下來就是直接開 Xcode 專案,Product -> Scheme 選「vChewingInstaller」,編譯即可。
|
||||
|
||||
> 之前說「在成功之後執行 `make` 即可組建、再執行 `make install` 可以觸發威注音的安裝程式」,這對新版威注音而言**當且僅當**使用純 Swift 編譯腳本工序時方可使用。目前的 libvchewing-data 模組已經針對 macOS 版威注音實裝了純 Swift 詞庫編譯腳本。
|
||||
|
||||
第一次安裝完,日後程式碼或詞庫有任何修改,只要重覆上述流程,再次安裝威注音即可。
|
||||
|
||||
要注意的是 macOS 可能會限制同一次 login session 能終結同一個輸入法的執行進程的次數(安裝程式透過 kill input method process 來讓新版的輸入法生效)。如果安裝若干次後,發現程式修改的結果並沒有出現、或甚至輸入法已無法再選用,只需要登出目前的 macOS 系統帳號、再重新登入即可。
|
||||
|
||||
補記: 該輸入法是在 2021 年 11 月初「28ae7deb4092f067539cff600397292e66a5dd56」這一版小麥注音建置的基礎上完成的。因為在清洗詞庫的時候清洗了全部的 git commit 歷史,所以無法自動從小麥注音官方倉庫上游繼承任何改動,只能手動同步任何在此之後的程式修正。最近一次同步參照是上游主倉庫的 2.2.2 版、以及 zonble 的分支「5cb6819e132a02bbcba77dbf083ada418750dab7」。
|
||||
|
||||
## 應用授權
|
||||
|
||||
威注音專案僅用到小麥注音的下述程式組件(MIT License):
|
||||
|
||||
- 狀態管理引擎 & NSStringUtils & FSEventStreamHelper (by Zonble Yang)。
|
||||
|
||||
- 半衰記憶模組,因故障暫時無法啟用 (by Mengjuei Hsieh)。
|
||||
|
||||
- 僅供研發人員調試方便而使用的 App 版安裝程式 (by Zonble Yang)。
|
||||
|
||||
- Voltaire MK2 選字窗、飄雲通知視窗、工具提示 (by Zonble Yang),有大幅度修改。
|
||||
|
||||
威注音輸入法 macOS 版以 MIT-NTL License 授權釋出 (與 MIT 相容):© 2021-2022 vChewing 專案。
|
||||
|
||||
- 威注音輸入法 macOS 版程式維護:Shiki Suen。特別感謝 Isaac Xen 與 Hiraku Wong 等人的技術協力。
|
||||
|
||||
- 鐵恨注音並擊處理引擎:Shiki Suen (MIT-NTL License)。
|
||||
|
||||
- 天權星語彙處理引擎:Shiki Suen (MIT-NTL License)。
|
||||
|
||||
- 威注音詞庫由 Shiki Suen 維護,以 3-Clause BSD License 授權釋出。其中的詞頻數據[由 NAER 授權用於非商業用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。
|
||||
|
||||
使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且一旦經過修改便不可以再繼續使用威注音的產品名稱。
|
||||
|
||||
## 格式規範等與參與研發時需要注意的事項:
|
||||
|
||||
該專案對源碼格式有規範,且 Swift 與其他 (Obj)C(++) 系語言持不同規範:
|
||||
|
||||
請洽該倉庫內的「[CONTRIBUTING.md](./CONTRIBUTING.md)」檔案。
|
||||
|
||||
## 其他
|
||||
|
||||
為了您的精神衛生,任何使用威注音輸入法時遇到的產品問題、請勿提報至小麥注音。哪怕您確信小麥注音也有該問題。
|
||||
|
||||
濫用沉默權來浪費對方的時間與熱情,也是一種暴力。**當對方最最最開始就把你當敵人的時候,你連呼吸都是錯的**。
|
||||
|
||||
$ EOF.
|
126
README.md
126
README.md
|
@ -1,119 +1,117 @@
|
|||
因不可控原因,該倉庫只能保證在 Gitee 有最新的內容可用:
|
||||
语言:*简体中文* | [繁體中文](./README-CHT.md)
|
||||
|
||||
- 下載:https://gitee.com/vchewing/vChewing-macOS/releases
|
||||
- 程式碼倉庫:https://gitee.com/vchewing/vChewing-macOS
|
||||
---
|
||||
|
||||
# vChewing 威注音輸入法
|
||||
因不可控原因,该仓库只能保证在 Gitee 有最新的内容可用:
|
||||
|
||||
威注音輸入法基於小麥注音二次開發,是**原生簡體中文、原生繁體中文注音輸入法**:
|
||||
- 下载:https://gitee.com/vchewing/vChewing-macOS/releases
|
||||
- 代码仓库:https://gitee.com/vchewing/vChewing-macOS
|
||||
|
||||
- 威注音是業界現階段支援注音排列種類數量與輸入用拼音種類數量最多的注音輸入法。
|
||||
- 受威注音自家的鐵恨注音並擊引擎加持。
|
||||
- 威注音的原廠詞庫內不存在任何可以妨礙該輸入法在世界上任何地方傳播的內容。
|
||||
- 相比中州韻(鼠須管)而言,威注音能夠做到真正的大千聲韻並擊。
|
||||
- 擁有拼音並擊模式,不懂注音的人群也可以受益於該輸入法所帶來的穩定的平均輸入速度。
|
||||
- 相比小鶴雙拼等雙拼方案而言,威注音雙手聲韻分工明確、且重碼率只有雙拼的五分之一。
|
||||
- 威注音對陸規審音完全相容:不熟悉台澎金馬審音的大陸用戶不會遇到與漢字讀音有關的不便。
|
||||
# vChewing 威注音输入法
|
||||
|
||||
威注音输入法基于小麦注音二次开发,是**原生简体中文/繁体中文注音输入法**:
|
||||
|
||||
- 威注音是业界现阶段支持注音排列种类数量与输入用拼音种类数量最多的注音输入法。
|
||||
- 受威注音自家的铁恨注音并击引擎加持。
|
||||
- 威注音的原厂词库内不存在任何可以妨碍该输入法在世界上任何地方传播的内容。
|
||||
- 相比中州韵(鼠须管)而言,威注音能够做到真正的大千声韵并击。
|
||||
- 拥有拼音并击模式,不懂注音的人群也可以受益于该输入法所带来的稳定的平均输入速度。
|
||||
- 相比小鹤双拼等双拼方案而言,威注音双手声韵分工明确、且重码率只有双拼的五分之一。
|
||||
- 威注音对陆规审音完全兼容:不熟悉台澎金马审音的大陆用户不会遇到与汉字读音有关的不便。
|
||||
- 反之亦然。
|
||||
|
||||
>威注音有很多特色功能。在此僅列舉部分:
|
||||
>- 支援 macOS 螢幕模擬鍵盤(僅傳統大千與傳統倚天佈局)。
|
||||
>- 可以將自己打的繁體中文自動轉成日本 JIS 新字體來輸出(包括基礎的字詞轉換)、也可以轉成康熙繁體來輸出。
|
||||
>- 簡繁體中文語料庫彼此分離,徹底杜絕任何繁簡轉換過程可能造成的失誤。
|
||||
>- 支援近年的全字庫漢字輸入。
|
||||
>- 可以自動整理使用者語彙檔案格式、自訂聯想詞。
|
||||
>威注音有很多特色功能。在此仅枚举部分:
|
||||
>- 支持 macOS 屏幕仿真键盘(仅传统大千与传统倚天布局)。
|
||||
>- 可以将自己打的繁体中文自动转成日本 JIS 新字体来输出(包括基础的字词转换),也可以转成康熙繁体来输出。
|
||||
>- 简繁体中文语料库彼此分离,彻底杜绝任何繁简转换过程可能造成的失误。
|
||||
>- 支持近年的全字库汉字输入。
|
||||
>- 可以自动整理用户语汇文档格式、自订联想词。
|
||||
>- ……
|
||||
|
||||
威注音分支專案及威注音詞庫由孫志貴(Shiki Suen)維護。小麥注音官方原始倉庫內的詞庫的內容均與孫志貴無關。
|
||||
威注音分支专案及威注音词库由孙志贵(Shiki Suen)维护。小麦注音官方原始仓库内的词库的内容均与孙志贵无关。
|
||||
|
||||
## 系統需求
|
||||
## 系统需求
|
||||
|
||||
建置用系統需求:至少 macOS 10.15 Catalina & Xcode 12。// 原因:Swift 封包管理支援所需。
|
||||
建置用系统需求:至少 macOS 10.15 Catalina & Xcode 12。// 原因:Swift 包管理器支持所需。
|
||||
|
||||
編譯出的成品對應系統需求:
|
||||
编译出的成品对应系统需求:
|
||||
|
||||
- 至少 macOS El Capitan 10.11.5,否則無法處理 Unicode 8.0 的漢字。即便如此,仍需手動升級蘋方至至少 macOS 10.12 開始隨贈的版本、以支援 Unicode 8.0 的通用規範漢字表用字(全字庫沒有「𫫇」字)。
|
||||
- 至少 macOS El Capitan 10.11.5,否则无法处理 Unicode 8.0 的汉字。即便如此,仍需手动升级苹方至至少 macOS 10.12 开始随附的版本、以支持 Unicode 8.0 的通用规范汉字表用字(全字库没有「𫫇」字)。
|
||||
|
||||
- 保留該系統支援的原因:非 Unibody 體型的 MacBook Pro 支援的最後一版 macOS 就是 El Capitan。
|
||||
- 保留该系统支持的原因:非 Unibody 体型的 MacBook Pro 支持的最后一版 macOS 就是 El Capitan。
|
||||
|
||||
- **推薦最低系統版本**:macOS 10.12 Sierra,對 Unicode 8.0 開始的《通用規範漢字表》漢字有原生的蘋方支援。
|
||||
- **推荐最低系统版本**:macOS 10.12 Sierra,对 Unicode 8.0 开始的《通用规范汉字表》汉字有原生的苹方支持。
|
||||
|
||||
- 同時建議**系統記憶體應至少 4GB**。威注音輸入法佔用記憶體約 115MB 左右(簡繁雙模式)、75MB左右(單模式),供參考。
|
||||
- 同时建议**系统运存应至少 4GB**。威注音输入法占用运存约 115MB 左右(简繁双模式)、75MB左右(单模式),供参考。
|
||||
|
||||
- 請務必使用 SSD 硬碟,否則可能會影響每次開機之後輸入法首次載入的速度。從 10.10 Yosemite 開始,macOS 就已經是針對機械硬碟負優化的作業系統了。
|
||||
- 请务必使用 SSD 硬盘,否则可能会影响每次开机之后输入法首次加载的速度。从 10.10 Yosemite 开始,macOS 就已经是针对机械硬盘负优化的操作系统了。
|
||||
|
||||
- 注:能裝 macOS 10.13 High Sierra 就不要去碰 macOS 10.12 Sierra 這個半成品。
|
||||
- 注:能装 macOS 10.13 High Sierra 就不要去碰 macOS 10.12 Sierra 这个半成品。
|
||||
|
||||
- 關於全字庫支援,因下述事實而在理論上很難做到最完美:
|
||||
- 关于全字库支持,因下述事实而在理论上很难做到最完美:
|
||||
|
||||
- 很可惜 GB18030-2005 並沒有官方提供的逐字讀音對照表,所以目前才用了全字庫。然而全字庫並不等於完美。
|
||||
- 很可惜 GB18030-2005 并没有官方提供的逐字读音对照表,所以目前才用了全字库。然而全字库并不等于完美。
|
||||
|
||||
- 有條件者可以安裝全字庫字型與花園明朝,否則全字庫等高萬國碼碼位漢字恐無法在輸入法的選字窗內完整顯示。
|
||||
- 有条件者可以安装全字库字体与花园明朝,否则全字库等高万国码码位汉字可能无法在输入法的选字窗内完整显示。
|
||||
|
||||
- 全字庫漢字顯示支援會受到具體系統版本對萬國碼版本的支援的限制。
|
||||
- 全字库汉字显示支持会受到具体系统版本对万国码版本的支持的限制。
|
||||
|
||||
- 有些全字庫漢字一開始會依賴萬國碼的私人造字區,且在之後被新版本萬國碼所支援。
|
||||
- 有些全字库汉字一开始会依赖万国码的私人造字区,且在之后被新版本万国码所支持。
|
||||
|
||||
## 建置流程
|
||||
|
||||
安裝 Xcode 之後,請先配置 Xcode 允許其直接構建在專案所在的資料夾下的 build 資料夾內。步驟:
|
||||
安装 Xcode 之后,请先配置 Xcode 允许其直接构建在专案所在的文档夹下的 build 文档夹内。步骤:
|
||||
```
|
||||
「Xcode」->「Preferences...」->「Locations」;
|
||||
「File」->「Project/WorkspaceSettings...」->「Advanced」;
|
||||
選「Custom」->「Relative to Workspace」即可。不選的話,make 的過程會出錯。
|
||||
选「Custom」->「Relative to Workspace」即可。否则,make 的过程会出错。
|
||||
```
|
||||
在終端機內定位到威注音的克隆本地專案的本地倉庫的目錄之後,執行 `make update` 以獲取最新詞庫。
|
||||
在终端机内定位到威注音的克隆本地专案的本地仓库的目录之后,运行 `make update` 以获取最新词库。
|
||||
|
||||
接下來就是直接開 Xcode 專案,Product -> Scheme 選「vChewingInstaller」,編譯即可。
|
||||
接下来就是直接开 Xcode 专案,Product -> Scheme 选「vChewingInstaller」,编译即可。
|
||||
|
||||
> 之前說「在成功之後執行 `make` 即可組建、再執行 `make install` 可以觸發威注音的安裝程式」,這對新版威注音而言**當且僅當**使用純 Swift 編譯腳本工序時方可使用。目前的 libvchewing-data 模組已經針對 macOS 版威注音實裝了純 Swift 詞庫編譯腳本。
|
||||
> 之前说「在成功之后运行 `make` 即可组建、再运行 `make install` 可以触发威注音的安装程序」,这对新版威注音而言**当且仅当**使用纯 Swift 编译脚本工序时方可使用。目前的 libvchewing-data 模块已经针对 macOS 版威注音实装了纯 Swift 词库编译脚本。
|
||||
|
||||
第一次安裝完,日後程式碼或詞庫有任何修改,只要重覆上述流程,再次安裝威注音即可。
|
||||
第一次安装完,日后代码或词库有任何修改,只要重复上述流程,再次安装威注音即可。
|
||||
|
||||
要注意的是 macOS 可能會限制同一次 login session 能終結同一個輸入法的執行進程的次數(安裝程式透過 kill input method process 來讓新版的輸入法生效)。如果安裝若干次後,發現程式修改的結果並沒有出現、或甚至輸入法已無法再選用,只需要登出目前的 macOS 系統帳號、再重新登入即可。
|
||||
要注意的是 macOS 可能会限制同一次 login session 能终结同一个输入法的进程的次数(安装程序通过 kill input method process 来让新版的输入法生效)。如果安装若干次后,发现程序修改的结果并没有出现、或甚至输入法已无法再选用,只需要登出目前的 macOS 系统帐号、再重新登入即可。
|
||||
|
||||
補記: 該輸入法是在 2021 年 11 月初「28ae7deb4092f067539cff600397292e66a5dd56」這一版小麥注音建置的基礎上完成的。因為在清洗詞庫的時候清洗了全部的 git commit 歷史,所以無法自動從小麥注音官方倉庫上游繼承任何改動,只能手動同步任何在此之後的程式修正。最近一次同步參照是上游主倉庫的 2.2.2 版、以及 zonble 的分支「5cb6819e132a02bbcba77dbf083ada418750dab7」。
|
||||
补记: 该输入法是在 2021 年 11 月初「28ae7deb4092f067539cff600397292e66a5dd56」这一版小麦注音建置的基础上完成的。因为在清洗词库的时候清洗了全部的 git commit 历史,所以无法自动从小麦注音官方仓库上游继承任何改动,只能手动同步任何在此之后的程序修正。最近一次同步参照是上游主仓库的 2.2.2 版、以及 zonble 的分支「5cb6819e132a02bbcba77dbf083ada418750dab7」。
|
||||
|
||||
## 應用授權
|
||||
## 应用授权
|
||||
|
||||
小麥注音引擎程式版權(MIT 授權):© 2011-2021 OpenVanilla 專案團隊(Mengjuei Hsieh, Lukhnos Liu, Zonble Yang, 等)。
|
||||
威注音专案仅用到小麦注音的下述程序组件(MIT License):
|
||||
|
||||
威注音專案僅用到小麥注音的下述程式組件:
|
||||
- 状态管理引擎 & NSStringUtils & FSEventStreamHelper (by Zonble Yang)。
|
||||
|
||||
- 狀態管理引擎 & NSStringUtils & FSEventStreamHelper (by Zonble Yang)。
|
||||
- 半衰记忆模块,因故障暂时无法激活 (by Mengjuei Hsieh)。
|
||||
|
||||
- 半衰記憶模組,因故障暫時無法啟用 (by Mengjuei Hsieh)。
|
||||
- 仅供研发人员调试方便而使用的 App 版安装程序 (by Zonble Yang)。
|
||||
|
||||
- 僅供研發人員調試方便而使用的 App 版安裝程式 (by Zonble Yang)。
|
||||
- Voltaire MK2 选字窗、飘云通知窗口、工具提示 (by Zonble Yang),有大幅度修改。
|
||||
|
||||
- Voltaire MK2 選字窗、飄雲通知視窗、工具提示 (by Zonble Yang),有大幅度修改。
|
||||
威注音输入法 macOS 版以 MIT-NTL License 授权发布 (与 MIT 兼容):© 2021-2022 vChewing 专案。
|
||||
|
||||
威注音輸入法 macOS 版以 MIT-NTL License 授權釋出 (與 MIT 相容):© 2021-2022 vChewing 專案。
|
||||
- 威注音输入法 macOS 版程序维护:Shiki Suen。特别感谢 Isaac Xen 与 Hiraku Wong 等人的技术协力。
|
||||
|
||||
- 威注音輸入法 macOS 版程式維護:Shiki Suen。特別感謝 Isaac Xen 與 Hiraku Wong 等人的技術協力。
|
||||
- 铁恨注音并击处理引擎:Shiki Suen (MIT-NTL License)。
|
||||
|
||||
- 鐵恨注音並擊處理引擎:Shiki Suen (MIT-NTL License)。
|
||||
- 天权星语汇处理引擎:Shiki Suen (MIT-NTL License)。
|
||||
|
||||
- 天權星語彙處理引擎:Shiki Suen (MIT-NTL License),針對「Lukhnos Liu 用 C++ 寫的 Gramambular」用 Swift 徹底重寫而成。
|
||||
- 威注音词库由 Shiki Suen 维护,以 3-Clause BSD License 授权发布。其中的词频数据[由 NAER 授权用于非商业用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。
|
||||
|
||||
- 威注音詞庫由 Shiki Suen 維護,以 3-Clause BSD License 授權釋出。其中的詞頻數據[由 NAER 授權用於非商業用途](https://twitter.com/ShikiSuen/status/1479329302713831424)。
|
||||
用户可自由使用、散播本软件,惟散播时必须完整保留版权声明及软件授权、且一旦经过修改便不可以再继续使用威注音的产品名称。
|
||||
|
||||
使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權、且一旦經過修改便不可以再繼續使用威注音的產品名稱。
|
||||
## 格式规范等与参与研发时需要注意的事项
|
||||
|
||||
## 格式規範等與參與研發時需要注意的事項:
|
||||
该专案对源码格式有规范,且 Swift 与其他 (Obj)C(++) 系语言持不同规范:
|
||||
|
||||
該專案對源碼格式有規範,且 Swift 與其他 (Obj)C(++) 系語言持不同規範:
|
||||
|
||||
請洽該倉庫內的「[CONTRIBUTING.md](./CONTRIBUTING.md)」檔案。
|
||||
请查看该仓库内的「[CONTRIBUTING.md](./CONTRIBUTING.md)」文档。
|
||||
|
||||
## 其他
|
||||
|
||||
為了您的精神衛生,任何使用威注音輸入法時遇到的產品問題、請勿提報至小麥注音,除非您確信小麥注音也有該問題。即便如此,也請在他們那邊不要提及威注音。
|
||||
为了您的精神卫生,任何使用威注音输入法时遇到的产品问题、请勿提报至小麦注音。哪怕您确信小麦注音也有该问题。
|
||||
|
||||
濫用沉默權來浪費對方的時間與熱情,也是一種暴力。**當對方最最最開始就把你當敵人的時候,你連呼吸都是錯的**。
|
||||
|
||||
其實我滿懷念上游專案還沒被 Lukhnos Liu 接管收入 OpenVanilla 的那個年代。MJHsieh 主導開發小麥注音的時候,且不討論他立場怎樣,但基礎的技術交流是完全沒問題的。LibChewing 那邊也是,正常交流完全沒問題。
|
||||
|
||||
有些事情,繼續爭論下去也沒用。本來我想著重寫 ctlInputMethod 撤掉 zonble 的狀態管理引擎的,畢竟有大陸同鄉寫的火山五筆輸入法的框架套上我的鐵恨注音並擊引擎可以直接用。但這樣賭氣對誰都沒好處。眼下,威注音 macOS 版還需要一些小維護。之後我就得開始考慮用 Windows 平台可用的除了 C++ 以外的語言重寫鐵恨注音並擊引擎與天權星語彙引擎、方便接下來威注音的 Windows 版本的研發。能將 Lukhnos 的 C++ 內容全部換掉、徹底砸碎套在威注音身上的名為 C++ 的枷鎖、讓威注音有一個自由的未來,我已經知足了。讓更多的人用上好用的輸入法,才是最重要的。**這個重要性,不是 zonble 用「私人需求」這種帽子扣過來、就可以泯滅了的**。
|
||||
滥用沉默权来浪费对方的时间与热情,也是一种暴力。**当对方最最最开始就把你当敌人的时候,你连呼吸都是错的**。
|
||||
|
||||
$ EOF.
|
||||
|
|
|
@ -23,8 +23,8 @@ import SwiftUI
|
|||
@available(macOS 10.15, *)
|
||||
extension Preferences {
|
||||
/**
|
||||
Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`.
|
||||
*/
|
||||
Function builder for `Preferences` components used in order to restrict types of child views to be of type `Section`.
|
||||
*/
|
||||
@resultBuilder
|
||||
public enum SectionBuilder {
|
||||
public static func buildBlock(_ sections: Section...) -> [Section] {
|
||||
|
@ -33,8 +33,8 @@ extension Preferences {
|
|||
}
|
||||
|
||||
/**
|
||||
A view which holds `Preferences.Section` views and does all the alignment magic similar to `NSGridView` from AppKit.
|
||||
*/
|
||||
A view which holds `Preferences.Section` views and does all the alignment magic similar to `NSGridView` from AppKit.
|
||||
*/
|
||||
public struct Container: View {
|
||||
private let sectionBuilder: () -> [Section]
|
||||
private let contentWidth: Double
|
||||
|
@ -42,15 +42,15 @@ extension Preferences {
|
|||
@State private var maximumLabelWidth = 0.0
|
||||
|
||||
/**
|
||||
Creates an instance of container component, which handles layout of stacked `Preferences.Section` views.
|
||||
Creates an instance of container component, which handles layout of stacked `Preferences.Section` views.
|
||||
|
||||
Custom alignment requires content width to be specified beforehand.
|
||||
Custom alignment requires content width to be specified beforehand.
|
||||
|
||||
- Parameters:
|
||||
- contentWidth: A fixed width of the container's content (excluding paddings).
|
||||
- minimumLabelWidth: A minimum width for labels within this container. By default, it will fit to the largest label.
|
||||
- builder: A view builder that creates `Preferences.Section`'s of this container.
|
||||
*/
|
||||
- Parameters:
|
||||
- contentWidth: A fixed width of the container's content (excluding paddings).
|
||||
- minimumLabelWidth: A minimum width for labels within this container. By default, it will fit to the largest label.
|
||||
- builder: A view builder that creates `Preferences.Section`'s of this container.
|
||||
*/
|
||||
public init(
|
||||
contentWidth: Double,
|
||||
minimumLabelWidth: Double = 0,
|
||||
|
|
|
@ -112,12 +112,12 @@ struct Localization {
|
|||
]
|
||||
|
||||
/**
|
||||
Returns the localized version of the given string.
|
||||
Returns the localized version of the given string.
|
||||
|
||||
- Parameter identifier: Identifier of the string to localize.
|
||||
- Parameter identifier: Identifier of the string to localize.
|
||||
|
||||
- Note: If the system's locale can't be determined, the English localization of the string will be returned.
|
||||
*/
|
||||
- Note: If the system's locale can't be determined, the English localization of the string will be returned.
|
||||
*/
|
||||
static subscript(identifier: Identifier) -> String {
|
||||
// Force-unwrapped since all of the involved code is under our control.
|
||||
let localizedDict = Localization.localizedStrings[identifier]!
|
||||
|
|
|
@ -25,18 +25,18 @@ import SwiftUI
|
|||
/// Acts as type-eraser for `Preferences.Pane<T>`.
|
||||
public protocol PreferencePaneConvertible {
|
||||
/**
|
||||
Convert `self` to equivalent `PreferencePane`.
|
||||
*/
|
||||
Convert `self` to equivalent `PreferencePane`.
|
||||
*/
|
||||
func asPreferencePane() -> PreferencePane
|
||||
}
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
extension Preferences {
|
||||
/**
|
||||
Create a SwiftUI-based preference pane.
|
||||
Create a SwiftUI-based preference pane.
|
||||
|
||||
SwiftUI equivalent of the `PreferencePane` protocol.
|
||||
*/
|
||||
SwiftUI equivalent of the `PreferencePane` protocol.
|
||||
*/
|
||||
public struct Pane<Content: View>: View, PreferencePaneConvertible {
|
||||
let identifier: PaneIdentifier
|
||||
let title: String
|
||||
|
@ -63,8 +63,8 @@ extension Preferences {
|
|||
}
|
||||
|
||||
/**
|
||||
Hosting controller enabling `Preferences.Pane` to be used alongside AppKit `NSViewController`'s.
|
||||
*/
|
||||
Hosting controller enabling `Preferences.Pane` to be used alongside AppKit `NSViewController`'s.
|
||||
*/
|
||||
public final class PaneHostingController<Content: View>: NSHostingController<Content>, PreferencePane {
|
||||
public let preferencePaneIdentifier: PaneIdentifier
|
||||
public let preferencePaneTitle: String
|
||||
|
@ -102,8 +102,8 @@ extension Preferences {
|
|||
@available(macOS 10.15, *)
|
||||
extension View {
|
||||
/**
|
||||
Applies font and color for a label used for describing a preference.
|
||||
*/
|
||||
Applies font and color for a label used for describing a preference.
|
||||
*/
|
||||
public func preferenceDescription() -> some View {
|
||||
font(.system(size: 11.0))
|
||||
// TODO: Use `.foregroundStyle` when targeting macOS 12.
|
||||
|
|
|
@ -97,17 +97,17 @@ public final class PreferencesWindowController: NSWindowController {
|
|||
}
|
||||
|
||||
/**
|
||||
Show the preferences window and brings it to front.
|
||||
Show the preferences window and brings it to front.
|
||||
|
||||
If you pass a `Preferences.PaneIdentifier`, the window will activate the corresponding tab.
|
||||
If you pass a `Preferences.PaneIdentifier`, the window will activate the corresponding tab.
|
||||
|
||||
- Parameter preferencePane: Identifier of the preference pane to display, or `nil` to show the tab that was open when the user last closed the window.
|
||||
- Parameter preferencePane: Identifier of the preference pane to display, or `nil` to show the tab that was open when the user last closed the window.
|
||||
|
||||
- Note: Unless you need to open a specific pane, prefer not to pass a parameter at all or `nil`.
|
||||
- Note: Unless you need to open a specific pane, prefer not to pass a parameter at all or `nil`.
|
||||
|
||||
- See `close()` to close the window again.
|
||||
- See `showWindow(_:)` to show the window without the convenience of activating the app.
|
||||
*/
|
||||
- See `close()` to close the window again.
|
||||
- See `showWindow(_:)` to show the window without the convenience of activating the app.
|
||||
*/
|
||||
public func show(preferencePane preferenceIdentifier: Preferences.PaneIdentifier? = nil) {
|
||||
if let preferenceIdentifier = preferenceIdentifier {
|
||||
tabViewController.activateTab(preferenceIdentifier: preferenceIdentifier, animated: false)
|
||||
|
@ -168,8 +168,8 @@ extension PreferencesWindowController {
|
|||
@available(macOS 10.15, *)
|
||||
extension PreferencesWindowController {
|
||||
/**
|
||||
Create a preferences window from only SwiftUI-based preference panes.
|
||||
*/
|
||||
Create a preferences window from only SwiftUI-based preference panes.
|
||||
*/
|
||||
public convenience init(
|
||||
panes: [PreferencePaneConvertible],
|
||||
style: Preferences.Style = .toolbarItems,
|
||||
|
|
|
@ -23,13 +23,13 @@ import SwiftUI
|
|||
@available(macOS 10.15, *)
|
||||
extension Preferences {
|
||||
/**
|
||||
Represents a section with right-aligned title and optional bottom divider.
|
||||
*/
|
||||
Represents a section with right-aligned title and optional bottom divider.
|
||||
*/
|
||||
@available(macOS 10.15, *)
|
||||
public struct Section: View {
|
||||
/**
|
||||
Preference key holding max width of section labels.
|
||||
*/
|
||||
Preference key holding max width of section labels.
|
||||
*/
|
||||
private struct LabelWidthPreferenceKey: PreferenceKey {
|
||||
typealias Value = Double
|
||||
|
||||
|
@ -42,8 +42,8 @@ extension Preferences {
|
|||
}
|
||||
|
||||
/**
|
||||
Convenience overlay for finding a label's dimensions using `GeometryReader`.
|
||||
*/
|
||||
Convenience overlay for finding a label's dimensions using `GeometryReader`.
|
||||
*/
|
||||
private struct LabelOverlay: View {
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
|
@ -54,8 +54,8 @@ extension Preferences {
|
|||
}
|
||||
|
||||
/**
|
||||
Convenience modifier for applying `LabelWidthPreferenceKey`.
|
||||
*/
|
||||
Convenience modifier for applying `LabelWidthPreferenceKey`.
|
||||
*/
|
||||
struct LabelWidthModifier: ViewModifier {
|
||||
@Binding var maximumWidth: Double
|
||||
|
||||
|
@ -73,15 +73,15 @@ extension Preferences {
|
|||
public let verticalAlignment: VerticalAlignment
|
||||
|
||||
/**
|
||||
A section is responsible for controlling a single preference.
|
||||
A section is responsible for controlling a single preference.
|
||||
|
||||
- Parameters:
|
||||
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
|
||||
- verticalAlignement: The vertical alignment of the section content.
|
||||
- verticalAlignment:
|
||||
- label: A view describing preference handled by this section.
|
||||
- content: A content view.
|
||||
*/
|
||||
- Parameters:
|
||||
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
|
||||
- verticalAlignement: The vertical alignment of the section content.
|
||||
- verticalAlignment:
|
||||
- label: A view describing preference handled by this section.
|
||||
- content: A content view.
|
||||
*/
|
||||
public init<Label: View, Content: View>(
|
||||
bottomDivider: Bool = false,
|
||||
verticalAlignment: VerticalAlignment = .firstTextBaseline,
|
||||
|
@ -98,15 +98,15 @@ extension Preferences {
|
|||
}
|
||||
|
||||
/**
|
||||
Creates instance of section, responsible for controling single preference with `Text` as a `Label`.
|
||||
Creates instance of section, responsible for controling single preference with `Text` as a `Label`.
|
||||
|
||||
- Parameters:
|
||||
- title: A string describing preference handled by this section.
|
||||
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
|
||||
- verticalAlignement: The vertical alignment of the section content.
|
||||
- verticalAlignment:
|
||||
- content: A content view.
|
||||
*/
|
||||
- Parameters:
|
||||
- title: A string describing preference handled by this section.
|
||||
- bottomDivider: Whether to place a `Divider` after the section content. Default is `false`.
|
||||
- verticalAlignement: The vertical alignment of the section content.
|
||||
- verticalAlignment:
|
||||
- content: A content view.
|
||||
*/
|
||||
public init<Content: View>(
|
||||
title: String,
|
||||
bottomDivider: Bool = false,
|
||||
|
|
|
@ -130,8 +130,8 @@ class UserInteractionPausableWindow: NSWindow {
|
|||
@available(macOS 10.15, *)
|
||||
extension View {
|
||||
/**
|
||||
Equivalent to `.eraseToAnyPublisher()` from the Combine framework.
|
||||
*/
|
||||
Equivalent to `.eraseToAnyPublisher()` from the Combine framework.
|
||||
*/
|
||||
func eraseToAnyView() -> AnyView {
|
||||
AnyView(self)
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 924c8f4156edf2f034b45f5f06d488e5e81d351c
|
||||
Subproject commit 06d16d8a468668278ad9f50cc8629983c490fa42
|
|
@ -74,7 +74,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
|
|||
mgrPrefs.setMissingDefaults()
|
||||
|
||||
// 只要使用者沒有勾選檢查更新、沒有主動做出要檢查更新的操作,就不要檢查更新。
|
||||
if (UserDefaults.standard.object(forKey: VersionUpdateApi.kCheckUpdateAutomatically) != nil) == true {
|
||||
if mgrPrefs.checkUpdateAutomatically {
|
||||
checkForUpdate()
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ctlNonModalAlertWindowDelega
|
|||
|
||||
// time for update?
|
||||
if !forced {
|
||||
if UserDefaults.standard.bool(forKey: VersionUpdateApi.kCheckUpdateAutomatically) == false {
|
||||
if !mgrPrefs.checkUpdateAutomatically {
|
||||
return
|
||||
}
|
||||
let now = Date()
|
||||
|
|
|
@ -110,15 +110,18 @@ enum KeyCodeBlackListed: UInt16 {
|
|||
}
|
||||
|
||||
// CharCodes: https://theasciicode.com.ar/ascii-control-characters/horizontal-tab-ascii-code-9.html
|
||||
enum CharCode: UInt /* 16 */ {
|
||||
case yajuusenpai = 114_514_191_191_810_893
|
||||
enum CharCode: UInt16 {
|
||||
case yajuusenpaiA = 114
|
||||
case yajuusenpaiB = 514
|
||||
case yajuusenpaiC = 1919
|
||||
case yajuusenpaiD = 810
|
||||
// CharCode is not reliable at all. KeyCode is the most appropriate choice due to its accuracy.
|
||||
// KeyCode doesn't give a phuque about the character sent through macOS keyboard layouts ...
|
||||
// ... but only focuses on which physical key is pressed.
|
||||
}
|
||||
|
||||
struct InputSignal: CustomStringConvertible {
|
||||
private(set) var useVerticalMode: Bool
|
||||
private(set) var isTypingVertical: Bool
|
||||
private(set) var inputText: String?
|
||||
private(set) var inputTextIgnoringModifiers: String?
|
||||
private(set) var charCode: UInt16
|
||||
|
@ -130,12 +133,12 @@ struct InputSignal: CustomStringConvertible {
|
|||
private var extraChooseCandidateKey: KeyCode = .kNone
|
||||
private var extraChooseCandidateKeyReverse: KeyCode = .kNone
|
||||
private var absorbedArrowKey: KeyCode = .kNone
|
||||
private var verticalModeOnlyChooseCandidateKey: KeyCode = .kNone
|
||||
private var verticalTypingOnlyChooseCandidateKey: KeyCode = .kNone
|
||||
private(set) var emacsKey: vChewingEmacsKey
|
||||
|
||||
public init(
|
||||
inputText: String?, keyCode: UInt16, charCode: UInt16, flags: NSEvent.ModifierFlags,
|
||||
isVerticalMode: Bool, inputTextIgnoringModifiers: String? = nil
|
||||
isVerticalTyping: Bool, inputTextIgnoringModifiers: String? = nil
|
||||
) {
|
||||
let inputText = AppleKeyboardConverter.cnvStringApple2ABC(inputText ?? "")
|
||||
let inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
|
@ -144,7 +147,7 @@ struct InputSignal: CustomStringConvertible {
|
|||
self.inputTextIgnoringModifiers = inputTextIgnoringModifiers
|
||||
self.flags = flags
|
||||
isFlagChanged = false
|
||||
useVerticalMode = isVerticalMode
|
||||
isTypingVertical = isVerticalTyping
|
||||
self.keyCode = keyCode
|
||||
self.charCode = AppleKeyboardConverter.cnvApple2ABC(charCode)
|
||||
emacsKey = EmacsKeyHelper.detect(
|
||||
|
@ -154,14 +157,14 @@ struct InputSignal: CustomStringConvertible {
|
|||
defineArrowKeys()
|
||||
}
|
||||
|
||||
public init(event: NSEvent, isVerticalMode: Bool) {
|
||||
public init(event: NSEvent, isVerticalTyping: Bool) {
|
||||
inputText = AppleKeyboardConverter.cnvStringApple2ABC(event.characters ?? "")
|
||||
inputTextIgnoringModifiers = AppleKeyboardConverter.cnvStringApple2ABC(
|
||||
event.charactersIgnoringModifiers ?? "")
|
||||
keyCode = event.keyCode
|
||||
flags = event.modifierFlags
|
||||
isFlagChanged = (event.type == .flagsChanged) ? true : false
|
||||
useVerticalMode = isVerticalMode
|
||||
isTypingVertical = isVerticalTyping
|
||||
let charCode: UInt16 = {
|
||||
// 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
guard let inputText = event.characters, !inputText.isEmpty else {
|
||||
|
@ -179,16 +182,16 @@ struct InputSignal: CustomStringConvertible {
|
|||
}
|
||||
|
||||
mutating func defineArrowKeys() {
|
||||
cursorForwardKey = useVerticalMode ? .kDownArrow : .kRightArrow
|
||||
cursorBackwardKey = useVerticalMode ? .kUpArrow : .kLeftArrow
|
||||
extraChooseCandidateKey = useVerticalMode ? .kLeftArrow : .kDownArrow
|
||||
extraChooseCandidateKeyReverse = useVerticalMode ? .kRightArrow : .kUpArrow
|
||||
absorbedArrowKey = useVerticalMode ? .kRightArrow : .kUpArrow
|
||||
verticalModeOnlyChooseCandidateKey = useVerticalMode ? absorbedArrowKey : .kNone
|
||||
cursorForwardKey = isTypingVertical ? .kDownArrow : .kRightArrow
|
||||
cursorBackwardKey = isTypingVertical ? .kUpArrow : .kLeftArrow
|
||||
extraChooseCandidateKey = isTypingVertical ? .kLeftArrow : .kDownArrow
|
||||
extraChooseCandidateKeyReverse = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
absorbedArrowKey = isTypingVertical ? .kRightArrow : .kUpArrow
|
||||
verticalTypingOnlyChooseCandidateKey = isTypingVertical ? absorbedArrowKey : .kNone
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"<inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalModeOnlyChooseCandidateKey:\(verticalModeOnlyChooseCandidateKey), emacsKey:\(emacsKey), useVerticalMode:\(useVerticalMode)>"
|
||||
"<inputText:\(String(describing: inputText)), inputTextIgnoringModifiers:\(String(describing: inputTextIgnoringModifiers)) charCode:\(charCode), keyCode:\(keyCode), flags:\(flags), cursorForwardKey:\(cursorForwardKey), cursorBackwardKey:\(cursorBackwardKey), extraChooseCandidateKey:\(extraChooseCandidateKey), extraChooseCandidateKeyReverse:\(extraChooseCandidateKeyReverse), absorbedArrowKey:\(absorbedArrowKey), verticalTypingOnlyChooseCandidateKey:\(verticalTypingOnlyChooseCandidateKey), emacsKey:\(emacsKey), isTypingVertical:\(isTypingVertical)>"
|
||||
}
|
||||
|
||||
// 除了 ANSI charCode 以外,其餘一律過濾掉,免得純 Swift 版 KeyHandler 被餵屎。
|
||||
|
@ -331,8 +334,8 @@ struct InputSignal: CustomStringConvertible {
|
|||
KeyCode(rawValue: keyCode) == extraChooseCandidateKeyReverse
|
||||
}
|
||||
|
||||
var isVerticalModeOnlyChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == verticalModeOnlyChooseCandidateKey
|
||||
var isverticalTypingOnlyChooseCandidateKey: Bool {
|
||||
KeyCode(rawValue: keyCode) == verticalTypingOnlyChooseCandidateKey
|
||||
}
|
||||
|
||||
var isUpperCaseASCIILetterKey: Bool {
|
||||
|
|
|
@ -110,9 +110,9 @@ class InputState {
|
|||
/// Represents that the composing buffer is not empty.
|
||||
class NotEmpty: InputState {
|
||||
private(set) var composingBuffer: String
|
||||
private(set) var cursorIndex: UInt
|
||||
private(set) var cursorIndex: Int = 0 { didSet { cursorIndex = max(cursorIndex, 0) } }
|
||||
|
||||
init(composingBuffer: String, cursorIndex: UInt) {
|
||||
init(composingBuffer: String, cursorIndex: Int) {
|
||||
self.composingBuffer = composingBuffer
|
||||
self.cursorIndex = cursorIndex
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ class InputState {
|
|||
var poppedText: String = ""
|
||||
var tooltip: String = ""
|
||||
|
||||
override init(composingBuffer: String, cursorIndex: UInt) {
|
||||
override init(composingBuffer: String, cursorIndex: Int) {
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
}
|
||||
|
||||
|
@ -151,91 +151,68 @@ class InputState {
|
|||
|
||||
// MARK: -
|
||||
|
||||
private let kMinMarkRangeLength = 2
|
||||
private let kMaxMarkRangeLength = mgrPrefs.maxCandidateLength
|
||||
|
||||
/// Represents that the user is marking a range in the composing buffer.
|
||||
class Marking: NotEmpty {
|
||||
private(set) var markerIndex: UInt
|
||||
private(set) var markedRange: NSRange
|
||||
private var allowedMarkRange: ClosedRange<Int> = mgrPrefs.minCandidateLength...mgrPrefs.maxCandidateLength
|
||||
private(set) var markerIndex: Int = 0 { didSet { markerIndex = max(markerIndex, 0) } }
|
||||
private(set) var markedRange: Range<Int>
|
||||
private var literalMarkedRange: Range<Int> {
|
||||
let lowerBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.lowerBound)
|
||||
let upperBoundLiteral = composingBuffer.charIndexLiteral(from: markedRange.upperBound)
|
||||
return lowerBoundLiteral..<upperBoundLiteral
|
||||
}
|
||||
|
||||
private var deleteTargetExists = false
|
||||
var tooltip: String {
|
||||
if composingBuffer.count != readings.count {
|
||||
TooltipController.backgroundColor = NSColor(
|
||||
red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
TooltipController.textColor = NSColor.white
|
||||
ctlInputMethod.tooltipController.setColor(state: .redAlert)
|
||||
return NSLocalizedString(
|
||||
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.", comment: ""
|
||||
)
|
||||
}
|
||||
|
||||
if mgrPrefs.phraseReplacementEnabled {
|
||||
TooltipController.backgroundColor = NSColor.purple
|
||||
TooltipController.textColor = NSColor.white
|
||||
ctlInputMethod.tooltipController.setColor(state: .warning)
|
||||
return NSLocalizedString(
|
||||
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry.", comment: ""
|
||||
)
|
||||
}
|
||||
if markedRange.length == 0 {
|
||||
if markedRange.isEmpty {
|
||||
return ""
|
||||
}
|
||||
|
||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||
if markedRange.length < kMinMarkRangeLength {
|
||||
TooltipController.backgroundColor = NSColor(
|
||||
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
|
||||
)
|
||||
TooltipController.textColor = NSColor(
|
||||
red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00
|
||||
)
|
||||
let text = composingBuffer.utf16SubString(with: markedRange)
|
||||
if literalMarkedRange.count < allowedMarkRange.lowerBound {
|
||||
ctlInputMethod.tooltipController.setColor(state: .denialInsufficiency)
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"\"%@\" length must ≥ 2 for a user phrase.", comment: ""
|
||||
), text
|
||||
)
|
||||
} else if markedRange.length > kMaxMarkRangeLength {
|
||||
TooltipController.backgroundColor = NSColor(
|
||||
red: 0.26, green: 0.16, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
TooltipController.textColor = NSColor(
|
||||
red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
} else if literalMarkedRange.count > allowedMarkRange.upperBound {
|
||||
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"\"%@\" length should ≤ %d for a user phrase.", comment: ""
|
||||
),
|
||||
text, kMaxMarkRangeLength
|
||||
text, allowedMarkRange.upperBound
|
||||
)
|
||||
}
|
||||
|
||||
let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location)
|
||||
let (exactEnd, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location + markedRange.length)
|
||||
let selectedReadings = readings[exactBegin..<exactEnd]
|
||||
let selectedReadings = readings[literalMarkedRange]
|
||||
let joined = selectedReadings.joined(separator: "-")
|
||||
let exist = mgrLangModel.checkIfUserPhraseExist(
|
||||
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined
|
||||
)
|
||||
if exist {
|
||||
deleteTargetExists = exist
|
||||
TooltipController.backgroundColor = NSColor(
|
||||
red: 0.00, green: 0.18, blue: 0.13, alpha: 1.00
|
||||
)
|
||||
TooltipController.textColor = NSColor(
|
||||
red: 0.00, green: 1.00, blue: 0.74, alpha: 1.00
|
||||
)
|
||||
ctlInputMethod.tooltipController.setColor(state: .prompt)
|
||||
return String(
|
||||
format: NSLocalizedString(
|
||||
"\"%@\" already exists: ENTER to boost, \n SHIFT+CMD+ENTER to exclude.", comment: ""
|
||||
), text
|
||||
)
|
||||
}
|
||||
TooltipController.backgroundColor = NSColor(
|
||||
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
|
||||
)
|
||||
TooltipController.textColor = NSColor.white
|
||||
ctlInputMethod.tooltipController.resetColor()
|
||||
return String(
|
||||
format: NSLocalizedString("\"%@\" selected. ENTER to add user phrase.", comment: ""),
|
||||
text
|
||||
|
@ -245,30 +222,34 @@ class InputState {
|
|||
var tooltipForInputting: String = ""
|
||||
private(set) var readings: [String]
|
||||
|
||||
init(composingBuffer: String, cursorIndex: UInt, markerIndex: UInt, readings: [String]) {
|
||||
init(composingBuffer: String, cursorIndex: Int, markerIndex: Int, readings: [String]) {
|
||||
self.markerIndex = markerIndex
|
||||
let begin = min(cursorIndex, markerIndex)
|
||||
let end = max(cursorIndex, markerIndex)
|
||||
markedRange = NSRange(location: Int(begin), length: Int(end - begin))
|
||||
markedRange = begin..<end
|
||||
self.readings = readings
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
}
|
||||
|
||||
var attributedString: NSAttributedString {
|
||||
let attributedString = NSMutableAttributedString(string: composingBuffer)
|
||||
let end = markedRange.location + markedRange.length
|
||||
let end = markedRange.upperBound
|
||||
|
||||
attributedString.setAttributes(
|
||||
[
|
||||
.underlineStyle: NSUnderlineStyle.single.rawValue,
|
||||
.markedClauseSegment: 0,
|
||||
], range: NSRange(location: 0, length: markedRange.location)
|
||||
], range: NSRange(location: 0, length: markedRange.lowerBound)
|
||||
)
|
||||
attributedString.setAttributes(
|
||||
[
|
||||
.underlineStyle: NSUnderlineStyle.thick.rawValue,
|
||||
.markedClauseSegment: 1,
|
||||
], range: markedRange
|
||||
],
|
||||
range: NSRange(
|
||||
location: markedRange.lowerBound,
|
||||
length: markedRange.upperBound - markedRange.lowerBound
|
||||
)
|
||||
)
|
||||
attributedString.setAttributes(
|
||||
[
|
||||
|
@ -277,7 +258,7 @@ class InputState {
|
|||
],
|
||||
range: NSRange(
|
||||
location: end,
|
||||
length: (composingBuffer as NSString).length - end
|
||||
length: composingBuffer.utf16.count - end
|
||||
)
|
||||
)
|
||||
return attributedString
|
||||
|
@ -298,56 +279,34 @@ class InputState {
|
|||
/// from the amount of Bopomofo readings. In this case, the range
|
||||
/// in the composing buffer and the readings could not match, so
|
||||
/// we disable the function to write user phrases in this case.
|
||||
if composingBuffer.count != readings.count {
|
||||
return false
|
||||
}
|
||||
if markedRange.length < kMinMarkRangeLength {
|
||||
return false
|
||||
}
|
||||
if markedRange.length > kMaxMarkRangeLength {
|
||||
return false
|
||||
}
|
||||
if ctlInputMethod.areWeDeleting, !deleteTargetExists {
|
||||
return false
|
||||
}
|
||||
return markedRange.length >= kMinMarkRangeLength
|
||||
&& markedRange.length <= kMaxMarkRangeLength
|
||||
/// 這裡的 deleteTargetExists 是防止使用者排除「詞庫內尚未存在的詞」,
|
||||
/// 免得使用者誤操作之後靠北「我怎麼敲不了這個詞?」之類的。
|
||||
((composingBuffer.count != readings.count)
|
||||
|| (ctlInputMethod.areWeDeleting && !deleteTargetExists))
|
||||
? false
|
||||
: allowedMarkRange.contains(literalMarkedRange.count)
|
||||
}
|
||||
|
||||
var chkIfUserPhraseExists: Bool {
|
||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||
let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location)
|
||||
let (exactEnd, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location + markedRange.length)
|
||||
let selectedReadings = readings[exactBegin..<exactEnd]
|
||||
let text = composingBuffer.utf16SubString(with: markedRange)
|
||||
let selectedReadings = readings[literalMarkedRange]
|
||||
let joined = selectedReadings.joined(separator: "-")
|
||||
return mgrLangModel.checkIfUserPhraseExist(
|
||||
userPhrase: text, mode: ctlInputMethod.currentKeyHandler.inputMode, key: joined
|
||||
)
|
||||
== true
|
||||
}
|
||||
|
||||
var userPhrase: String {
|
||||
let text = (composingBuffer as NSString).substring(with: markedRange)
|
||||
let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location)
|
||||
let (exactEnd, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location + markedRange.length)
|
||||
let selectedReadings = readings[exactBegin..<exactEnd]
|
||||
let text = composingBuffer.utf16SubString(with: markedRange)
|
||||
let selectedReadings = readings[literalMarkedRange]
|
||||
let joined = selectedReadings.joined(separator: "-")
|
||||
return "\(text) \(joined)"
|
||||
}
|
||||
|
||||
var userPhraseConverted: String {
|
||||
let text =
|
||||
OpenCCBridge.crossConvert(
|
||||
(composingBuffer as NSString).substring(with: markedRange)) ?? ""
|
||||
let (exactBegin, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location)
|
||||
let (exactEnd, _) = (composingBuffer as NSString).characterIndex(
|
||||
from: markedRange.location + markedRange.length)
|
||||
let selectedReadings = readings[exactBegin..<exactEnd]
|
||||
OpenCCBridge.crossConvert(composingBuffer.utf16SubString(with: markedRange)) ?? ""
|
||||
let selectedReadings = readings[literalMarkedRange]
|
||||
let joined = selectedReadings.joined(separator: "-")
|
||||
let convertedMark = "#𝙊𝙥𝙚𝙣𝘾𝘾"
|
||||
return "\(text) \(joined)\t\(convertedMark)"
|
||||
|
@ -359,11 +318,11 @@ class InputState {
|
|||
/// Represents that the user is choosing in a candidates list.
|
||||
class ChoosingCandidate: NotEmpty {
|
||||
private(set) var candidates: [String]
|
||||
private(set) var useVerticalMode: Bool
|
||||
private(set) var isTypingVertical: Bool
|
||||
|
||||
init(composingBuffer: String, cursorIndex: UInt, candidates: [String], useVerticalMode: Bool) {
|
||||
init(composingBuffer: String, cursorIndex: Int, candidates: [String], isTypingVertical: Bool) {
|
||||
self.candidates = candidates
|
||||
self.useVerticalMode = useVerticalMode
|
||||
self.isTypingVertical = isTypingVertical
|
||||
super.init(composingBuffer: composingBuffer, cursorIndex: cursorIndex)
|
||||
}
|
||||
|
||||
|
@ -379,7 +338,7 @@ class InputState {
|
|||
}
|
||||
|
||||
override var description: String {
|
||||
"<InputState.ChoosingCandidate, candidates:\(candidates), useVerticalMode:\(useVerticalMode), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||
"<InputState.ChoosingCandidate, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,27 +348,27 @@ class InputState {
|
|||
/// in the associated phrases mode.
|
||||
class AssociatedPhrases: InputState {
|
||||
private(set) var candidates: [String] = []
|
||||
private(set) var useVerticalMode: Bool = false
|
||||
init(candidates: [String], useVerticalMode: Bool) {
|
||||
private(set) var isTypingVertical: Bool = false
|
||||
init(candidates: [String], isTypingVertical: Bool) {
|
||||
self.candidates = candidates
|
||||
self.useVerticalMode = useVerticalMode
|
||||
self.isTypingVertical = isTypingVertical
|
||||
super.init()
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"<InputState.AssociatedPhrases, candidates:\(candidates), useVerticalMode:\(useVerticalMode)>"
|
||||
"<InputState.AssociatedPhrases, candidates:\(candidates), isTypingVertical:\(isTypingVertical)>"
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolTable: ChoosingCandidate {
|
||||
var node: SymbolNode
|
||||
|
||||
init(node: SymbolNode, useVerticalMode: Bool) {
|
||||
init(node: SymbolNode, isTypingVertical: Bool) {
|
||||
self.node = node
|
||||
let candidates = node.children?.map(\.title) ?? [String]()
|
||||
super.init(
|
||||
composingBuffer: "", cursorIndex: 0, candidates: candidates,
|
||||
useVerticalMode: useVerticalMode
|
||||
isTypingVertical: isTypingVertical
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -430,7 +389,7 @@ class InputState {
|
|||
}
|
||||
|
||||
override var description: String {
|
||||
"<InputState.SymbolTable, candidates:\(candidates), useVerticalMode:\(useVerticalMode), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||
"<InputState.SymbolTable, candidates:\(candidates), isTypingVertical:\(isTypingVertical), composingBuffer:\(composingBuffer), cursorIndex:\(cursorIndex)>"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@ public enum InputMode: String {
|
|||
// MARK: - Delegate.
|
||||
|
||||
protocol KeyHandlerDelegate {
|
||||
func ctlCandidate(for _: KeyHandler) -> Any
|
||||
func ctlCandidate() -> ctlCandidate
|
||||
func keyHandler(
|
||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||
ctlCandidate controller: Any
|
||||
ctlCandidate controller: ctlCandidate
|
||||
)
|
||||
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputState)
|
||||
-> Bool
|
||||
|
|
|
@ -37,7 +37,7 @@ extension KeyHandler {
|
|||
) -> Bool {
|
||||
let inputText = input.inputText
|
||||
let charCode: UniChar = input.charCode
|
||||
if let ctlCandidateCurrent = delegate!.ctlCandidate(for: self) as? ctlCandidate {
|
||||
if let ctlCandidateCurrent = delegate?.ctlCandidate() {
|
||||
// MARK: Cancel Candidate
|
||||
|
||||
let cancelCandidateKey =
|
||||
|
@ -71,7 +71,7 @@ extension KeyHandler {
|
|||
}
|
||||
delegate!.keyHandler(
|
||||
self,
|
||||
didSelectCandidateAt: Int(ctlCandidateCurrent.selectedCandidateIndex),
|
||||
didSelectCandidateAt: ctlCandidateCurrent.selectedCandidateIndex,
|
||||
ctlCandidate: ctlCandidateCurrent
|
||||
)
|
||||
return true
|
||||
|
@ -138,18 +138,21 @@ extension KeyHandler {
|
|||
// MARK: Left Arrow
|
||||
|
||||
if input.isLeft {
|
||||
if ctlCandidateCurrent is ctlCandidateHorizontal {
|
||||
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("1145148D")
|
||||
errorCallback()
|
||||
}
|
||||
} else {
|
||||
let updated: Bool = ctlCandidateCurrent.showPreviousPage()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("1919810D")
|
||||
errorCallback()
|
||||
}
|
||||
switch ctlCandidateCurrent.currentLayout {
|
||||
case .horizontal:
|
||||
do {
|
||||
if !ctlCandidateCurrent.highlightPreviousCandidate() {
|
||||
IME.prtDebugIntel("1145148D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
case .vertical:
|
||||
do {
|
||||
if !ctlCandidateCurrent.showPreviousPage() {
|
||||
IME.prtDebugIntel("1919810D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -168,18 +171,21 @@ extension KeyHandler {
|
|||
// MARK: Right Arrow
|
||||
|
||||
if input.isRight {
|
||||
if ctlCandidateCurrent is ctlCandidateHorizontal {
|
||||
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("9B65138D")
|
||||
errorCallback()
|
||||
}
|
||||
} else {
|
||||
let updated: Bool = ctlCandidateCurrent.showNextPage()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("9244908D")
|
||||
errorCallback()
|
||||
}
|
||||
switch ctlCandidateCurrent.currentLayout {
|
||||
case .horizontal:
|
||||
do {
|
||||
if !ctlCandidateCurrent.highlightNextCandidate() {
|
||||
IME.prtDebugIntel("9B65138D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
case .vertical:
|
||||
do {
|
||||
if !ctlCandidateCurrent.showNextPage() {
|
||||
IME.prtDebugIntel("9244908D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -198,18 +204,21 @@ extension KeyHandler {
|
|||
// MARK: Up Arrow
|
||||
|
||||
if input.isUp {
|
||||
if ctlCandidateCurrent is ctlCandidateHorizontal {
|
||||
let updated: Bool = ctlCandidateCurrent.showPreviousPage()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("9B614524")
|
||||
errorCallback()
|
||||
}
|
||||
} else {
|
||||
let updated: Bool = ctlCandidateCurrent.highlightPreviousCandidate()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("ASD9908D")
|
||||
errorCallback()
|
||||
}
|
||||
switch ctlCandidateCurrent.currentLayout {
|
||||
case .horizontal:
|
||||
do {
|
||||
if !ctlCandidateCurrent.showPreviousPage() {
|
||||
IME.prtDebugIntel("9B614524")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
case .vertical:
|
||||
do {
|
||||
if !ctlCandidateCurrent.highlightPreviousCandidate() {
|
||||
IME.prtDebugIntel("ASD9908D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -217,18 +226,21 @@ extension KeyHandler {
|
|||
// MARK: Down Arrow
|
||||
|
||||
if input.isDown {
|
||||
if ctlCandidateCurrent is ctlCandidateHorizontal {
|
||||
let updated: Bool = ctlCandidateCurrent.showNextPage()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("92B990DD")
|
||||
errorCallback()
|
||||
}
|
||||
} else {
|
||||
let updated: Bool = ctlCandidateCurrent.highlightNextCandidate()
|
||||
if !updated {
|
||||
IME.prtDebugIntel("6B99908D")
|
||||
errorCallback()
|
||||
}
|
||||
switch ctlCandidateCurrent.currentLayout {
|
||||
case .horizontal:
|
||||
do {
|
||||
if !ctlCandidateCurrent.showNextPage() {
|
||||
IME.prtDebugIntel("92B990DD")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
case .vertical:
|
||||
do {
|
||||
if !ctlCandidateCurrent.highlightNextCandidate() {
|
||||
IME.prtDebugIntel("6B99908D")
|
||||
errorCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -260,11 +272,11 @@ extension KeyHandler {
|
|||
return false
|
||||
} else { // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。
|
||||
if input.isEnd || input.emacsKey == vChewingEmacsKey.end {
|
||||
if ctlCandidateCurrent.selectedCandidateIndex == UInt(candidates.count - 1) {
|
||||
if ctlCandidateCurrent.selectedCandidateIndex == candidates.count - 1 {
|
||||
IME.prtDebugIntel("9B69AAAD")
|
||||
errorCallback()
|
||||
} else {
|
||||
ctlCandidateCurrent.selectedCandidateIndex = UInt(candidates.count - 1)
|
||||
ctlCandidateCurrent.selectedCandidateIndex = candidates.count - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,10 +306,10 @@ extension KeyHandler {
|
|||
}
|
||||
|
||||
if index != NSNotFound {
|
||||
let candidateIndex: UInt = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(UInt(index))
|
||||
if candidateIndex != UInt.max {
|
||||
let candidateIndex = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(index)
|
||||
if candidateIndex != Int.max {
|
||||
delegate!.keyHandler(
|
||||
self, didSelectCandidateAt: Int(candidateIndex), ctlCandidate: ctlCandidateCurrent
|
||||
self, didSelectCandidateAt: candidateIndex, ctlCandidate: ctlCandidateCurrent
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
@ -342,11 +354,11 @@ extension KeyHandler {
|
|||
}
|
||||
|
||||
if shouldAutoSelectCandidate {
|
||||
let candidateIndex: UInt = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(0)
|
||||
if candidateIndex != UInt.max {
|
||||
let candidateIndex = ctlCandidateCurrent.candidateIndexAtKeyLabelIndex(0)
|
||||
if candidateIndex != Int.max {
|
||||
delegate!.keyHandler(
|
||||
self,
|
||||
didSelectCandidateAt: Int(candidateIndex),
|
||||
didSelectCandidateAt: candidateIndex,
|
||||
ctlCandidate: ctlCandidateCurrent
|
||||
)
|
||||
clear()
|
||||
|
|
|
@ -200,7 +200,7 @@ extension KeyHandler {
|
|||
if mgrPrefs.useSCPCTypingMode {
|
||||
let choosingCandidates: InputState.ChoosingCandidate = buildCandidate(
|
||||
state: inputting,
|
||||
useVerticalMode: input.useVerticalMode
|
||||
isTypingVertical: input.isTypingVertical
|
||||
)
|
||||
if choosingCandidates.candidates.count == 1 {
|
||||
clear()
|
||||
|
@ -213,7 +213,7 @@ extension KeyHandler {
|
|||
if let associatedPhrases =
|
||||
buildAssociatePhraseState(
|
||||
withKey: text,
|
||||
useVerticalMode: input.useVerticalMode
|
||||
isTypingVertical: input.isTypingVertical
|
||||
), !associatedPhrases.candidates.isEmpty
|
||||
{
|
||||
stateCallback(associatedPhrases)
|
||||
|
@ -243,7 +243,7 @@ extension KeyHandler {
|
|||
if let currentState = state as? InputState.NotEmpty, _composer.isEmpty,
|
||||
input.isExtraChooseCandidateKey || input.isExtraChooseCandidateKeyReverse || input.isSpace
|
||||
|| input.isPageDown || input.isPageUp || (input.isTab && mgrPrefs.specifyShiftTabKeyBehavior)
|
||||
|| (input.useVerticalMode && (input.isVerticalModeOnlyChooseCandidateKey))
|
||||
|| (input.isTypingVertical && (input.isverticalTypingOnlyChooseCandidateKey))
|
||||
{
|
||||
if input.isSpace {
|
||||
// If the Space key is NOT set to be a selection key
|
||||
|
@ -266,7 +266,7 @@ extension KeyHandler {
|
|||
return true
|
||||
}
|
||||
}
|
||||
stateCallback(buildCandidate(state: currentState, useVerticalMode: input.useVerticalMode))
|
||||
stateCallback(buildCandidate(state: currentState, isTypingVertical: input.isTypingVertical))
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ extension KeyHandler {
|
|||
let inputting = buildInputtingState
|
||||
inputting.poppedText = poppedText
|
||||
stateCallback(inputting)
|
||||
stateCallback(buildCandidate(state: inputting, useVerticalMode: input.useVerticalMode))
|
||||
stateCallback(buildCandidate(state: inputting, isTypingVertical: input.isTypingVertical))
|
||||
} else { // If there is still unfinished bpmf reading, ignore the punctuation
|
||||
IME.prtDebugIntel("17446655")
|
||||
errorCallback()
|
||||
|
@ -380,7 +380,7 @@ extension KeyHandler {
|
|||
// 於是這裡用「模擬一次 Enter 鍵的操作」使其代為執行這個 commit buffer 的動作。
|
||||
// 這裡不需要該函數所傳回的 bool 結果,所以用「_ =」解消掉。
|
||||
_ = handleEnter(state: state, stateCallback: stateCallback, errorCallback: errorCallback)
|
||||
stateCallback(InputState.SymbolTable(node: SymbolNode.root, useVerticalMode: input.useVerticalMode))
|
||||
stateCallback(InputState.SymbolTable(node: SymbolNode.root, isTypingVertical: input.isTypingVertical))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ extension KeyHandler {
|
|||
if handlePunctuation(
|
||||
customPunctuation,
|
||||
state: state,
|
||||
usingVerticalMode: input.useVerticalMode,
|
||||
usingVerticalTyping: input.isTypingVertical,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
|
@ -425,7 +425,7 @@ extension KeyHandler {
|
|||
if handlePunctuation(
|
||||
punctuation,
|
||||
state: state,
|
||||
usingVerticalMode: input.useVerticalMode,
|
||||
usingVerticalTyping: input.isTypingVertical,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
|
@ -438,7 +438,7 @@ extension KeyHandler {
|
|||
if handlePunctuation(
|
||||
letter,
|
||||
state: state,
|
||||
usingVerticalMode: input.useVerticalMode,
|
||||
usingVerticalTyping: input.isTypingVertical,
|
||||
stateCallback: stateCallback,
|
||||
errorCallback: errorCallback
|
||||
) {
|
||||
|
|
|
@ -34,18 +34,18 @@ extension KeyHandler {
|
|||
var buildInputtingState: InputState.Inputting {
|
||||
// "Updating the composing buffer" means to request the client
|
||||
// to "refresh" the text input buffer with our "composing text"
|
||||
var tooltipParameterRef: [String] = ["", ""]
|
||||
var composingBuffer = ""
|
||||
var composedStringCursorIndex = 0
|
||||
var readingCursorIndex = 0
|
||||
// We must do some Unicode codepoint counting to find the actual cursor location for the client
|
||||
// i.e. we need to take UTF-16 into consideration, for which a surrogate pair takes 2 UniChars
|
||||
// locations. These processes are inherited from the ObjC++ version of this class and might be
|
||||
// unnecessary in Swift, but this deduction requires further experiments.
|
||||
// locations. Since we are using Swift, we use .utf16 as the equivalent of NSString.length().
|
||||
for walkedNode in _walkedNodes {
|
||||
if let theNode = walkedNode.node {
|
||||
let strNodeValue = theNode.currentKeyValue.value
|
||||
composingBuffer += strNodeValue
|
||||
let arrSplit: [NSString] = (strNodeValue as NSString).split()
|
||||
let arrSplit: [String] = Array(strNodeValue).map { String($0) }
|
||||
let codepointCount = arrSplit.count
|
||||
// This re-aligns the cursor index in the composed string
|
||||
// (the actual cursor on the screen) with the builder's logical
|
||||
|
@ -55,23 +55,39 @@ extension KeyHandler {
|
|||
// index.
|
||||
let spanningLength: Int = walkedNode.spanningLength
|
||||
if readingCursorIndex + spanningLength <= builderCursorIndex {
|
||||
composedStringCursorIndex += (strNodeValue as NSString).length
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
} else {
|
||||
if codepointCount == spanningLength {
|
||||
var i = 0
|
||||
while i < codepointCount, readingCursorIndex < builderCursorIndex {
|
||||
composedStringCursorIndex += arrSplit[i].length
|
||||
composedStringCursorIndex += arrSplit[i].utf16.count
|
||||
readingCursorIndex += 1
|
||||
i += 1
|
||||
}
|
||||
} else {
|
||||
if readingCursorIndex < builderCursorIndex {
|
||||
composedStringCursorIndex += (strNodeValue as NSString).length
|
||||
composedStringCursorIndex += strNodeValue.utf16.count
|
||||
readingCursorIndex += spanningLength
|
||||
if readingCursorIndex > builderCursorIndex {
|
||||
readingCursorIndex = builderCursorIndex
|
||||
}
|
||||
// Now we start preparing the contents of the tooltips used
|
||||
// in cases of moving cursors across certain emojis which emoji
|
||||
// char count is inequal to the reading count.
|
||||
// Example in McBopomofo: Typing 王建民 (3 readings) gets a tree emoji.
|
||||
// Example in vChewing: Typing 義麵 (2 readings) gets a pasta emoji.
|
||||
switch builderCursorIndex {
|
||||
case _builder.readings.count...:
|
||||
tooltipParameterRef[0] = _builder.readings[_builder.readings.count - 1]
|
||||
case 0:
|
||||
tooltipParameterRef[1] = _builder.readings[builderCursorIndex]
|
||||
default:
|
||||
do {
|
||||
tooltipParameterRef[0] = _builder.readings[builderCursorIndex - 1]
|
||||
tooltipParameterRef[1] = _builder.readings[builderCursorIndex]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,26 +97,63 @@ extension KeyHandler {
|
|||
// Now, we gather all the intel, separate the composing buffer to two parts (head and tail),
|
||||
// and insert the reading text (the Mandarin syllable) in between them.
|
||||
// The reading text is what the user is typing.
|
||||
let head = String((composingBuffer as NSString).substring(to: composedStringCursorIndex))
|
||||
let reading = _composer.getInlineCompositionForIMK(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
|
||||
let tail = String((composingBuffer as NSString).substring(from: composedStringCursorIndex))
|
||||
let composedText = head + reading + tail
|
||||
let cursorIndex = composedStringCursorIndex + reading.count
|
||||
var arrHead = [String.UTF16View.Element]()
|
||||
var arrTail = [String.UTF16View.Element]()
|
||||
|
||||
return InputState.Inputting(composingBuffer: composedText, cursorIndex: UInt(cursorIndex))
|
||||
for (i, n) in composingBuffer.utf16.enumerated() {
|
||||
if i < composedStringCursorIndex {
|
||||
arrHead.append(n)
|
||||
} else {
|
||||
arrTail.append(n)
|
||||
}
|
||||
}
|
||||
|
||||
let head = String(utf16CodeUnits: arrHead, count: arrHead.count)
|
||||
let reading = _composer.getInlineCompositionForIMK(isHanyuPinyin: mgrPrefs.showHanyuPinyinInCompositionBuffer)
|
||||
let tail = String(utf16CodeUnits: arrTail, count: arrTail.count)
|
||||
let composedText = head + reading + tail
|
||||
let cursorIndex = composedStringCursorIndex + reading.utf16.count
|
||||
|
||||
let stateResult = InputState.Inputting(composingBuffer: composedText, cursorIndex: cursorIndex)
|
||||
|
||||
// Now we start weaving the contents of the tooltip.
|
||||
if tooltipParameterRef[0].isEmpty, tooltipParameterRef[1].isEmpty {
|
||||
stateResult.tooltip = ""
|
||||
} else if tooltipParameterRef[0].isEmpty {
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is to the rear of \"%@\".", comment: ""),
|
||||
tooltipParameterRef[1]
|
||||
)
|
||||
} else if tooltipParameterRef[1].isEmpty {
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is in front of \"%@\".", comment: ""),
|
||||
tooltipParameterRef[0]
|
||||
)
|
||||
} else {
|
||||
stateResult.tooltip = String(
|
||||
format: NSLocalizedString("Cursor is between \"%@\" and \"%@\".", comment: ""),
|
||||
tooltipParameterRef[0], tooltipParameterRef[1]
|
||||
)
|
||||
}
|
||||
|
||||
if !stateResult.tooltip.isEmpty {
|
||||
ctlInputMethod.tooltipController.setColor(state: .denialOverflow)
|
||||
}
|
||||
|
||||
return stateResult
|
||||
}
|
||||
|
||||
// MARK: - 用以生成候選詞陣列及狀態
|
||||
|
||||
func buildCandidate(
|
||||
state currentState: InputState.NotEmpty,
|
||||
useVerticalMode: Bool = false
|
||||
isTypingVertical: Bool = false
|
||||
) -> InputState.ChoosingCandidate {
|
||||
InputState.ChoosingCandidate(
|
||||
composingBuffer: currentState.composingBuffer,
|
||||
cursorIndex: currentState.cursorIndex,
|
||||
candidates: candidatesArray,
|
||||
useVerticalMode: useVerticalMode
|
||||
isTypingVertical: isTypingVertical
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -115,11 +168,11 @@ extension KeyHandler {
|
|||
// 是否為空:如果陣列為空的話,直接回呼一個空狀態。
|
||||
func buildAssociatePhraseState(
|
||||
withKey key: String!,
|
||||
useVerticalMode: Bool
|
||||
isTypingVertical: Bool
|
||||
) -> InputState.AssociatedPhrases! {
|
||||
// 上一行必須要用驚嘆號,否則 Xcode 會誤導你砍掉某些實際上必需的語句。
|
||||
InputState.AssociatedPhrases(
|
||||
candidates: buildAssociatePhraseArray(withKey: key), useVerticalMode: useVerticalMode
|
||||
candidates: buildAssociatePhraseArray(withKey: key), isTypingVertical: isTypingVertical
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -153,7 +206,7 @@ extension KeyHandler {
|
|||
if input.isCursorBackward || input.emacsKey == vChewingEmacsKey.backward, input.isShiftHold {
|
||||
var index = state.markerIndex
|
||||
if index > 0 {
|
||||
index = UInt((state.composingBuffer as NSString).previousUtf16Position(for: Int(index)))
|
||||
index = state.composingBuffer.utf16PreviousPosition(for: index)
|
||||
let marking = InputState.Marking(
|
||||
composingBuffer: state.composingBuffer,
|
||||
cursorIndex: state.cursorIndex,
|
||||
|
@ -161,7 +214,7 @@ extension KeyHandler {
|
|||
readings: state.readings
|
||||
)
|
||||
marking.tooltipForInputting = state.tooltipForInputting
|
||||
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
|
||||
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
} else {
|
||||
IME.prtDebugIntel("1149908D")
|
||||
errorCallback()
|
||||
|
@ -173,10 +226,8 @@ extension KeyHandler {
|
|||
// Shift + Right
|
||||
if input.isCursorForward || input.emacsKey == vChewingEmacsKey.forward, input.isShiftHold {
|
||||
var index = state.markerIndex
|
||||
// 這裡繼續用 NSString 是為了與 Zonble 之前引入的 NSStringUtils 相容。
|
||||
// 不然的話,這行判斷會失敗、引發「9B51408D」錯誤。
|
||||
if index < ((state.composingBuffer as NSString).length) {
|
||||
index = UInt((state.composingBuffer as NSString).nextUtf16Position(for: Int(index)))
|
||||
if index < (state.composingBuffer.utf16.count) {
|
||||
index = state.composingBuffer.utf16NextPosition(for: index)
|
||||
let marking = InputState.Marking(
|
||||
composingBuffer: state.composingBuffer,
|
||||
cursorIndex: state.cursorIndex,
|
||||
|
@ -184,7 +235,7 @@ extension KeyHandler {
|
|||
readings: state.readings
|
||||
)
|
||||
marking.tooltipForInputting = state.tooltipForInputting
|
||||
stateCallback(marking.markedRange.length == 0 ? marking.convertedToInputting : marking)
|
||||
stateCallback(marking.markedRange.isEmpty ? marking.convertedToInputting : marking)
|
||||
} else {
|
||||
IME.prtDebugIntel("9B51408D")
|
||||
errorCallback()
|
||||
|
@ -200,7 +251,7 @@ extension KeyHandler {
|
|||
func handlePunctuation(
|
||||
_ customPunctuation: String,
|
||||
state: InputState,
|
||||
usingVerticalMode useVerticalMode: Bool,
|
||||
usingVerticalTyping isTypingVertical: Bool,
|
||||
stateCallback: @escaping (InputState) -> Void,
|
||||
errorCallback: @escaping () -> Void
|
||||
) -> Bool {
|
||||
|
@ -218,7 +269,7 @@ extension KeyHandler {
|
|||
if mgrPrefs.useSCPCTypingMode, _composer.isEmpty {
|
||||
let candidateState = buildCandidate(
|
||||
state: inputting,
|
||||
useVerticalMode: useVerticalMode
|
||||
isTypingVertical: isTypingVertical
|
||||
)
|
||||
if candidateState.candidates.count == 1 {
|
||||
clear()
|
||||
|
@ -512,13 +563,13 @@ extension KeyHandler {
|
|||
|
||||
if input.isShiftHold {
|
||||
// Shift + Right
|
||||
if currentState.cursorIndex < (currentState.composingBuffer as NSString).length {
|
||||
let nextPosition = (currentState.composingBuffer as NSString).nextUtf16Position(
|
||||
for: Int(currentState.cursorIndex))
|
||||
if currentState.cursorIndex < currentState.composingBuffer.utf16.count {
|
||||
let nextPosition = currentState.composingBuffer.utf16NextPosition(
|
||||
for: currentState.cursorIndex)
|
||||
let marking: InputState.Marking! = InputState.Marking(
|
||||
composingBuffer: currentState.composingBuffer,
|
||||
cursorIndex: currentState.cursorIndex,
|
||||
markerIndex: UInt(nextPosition),
|
||||
markerIndex: nextPosition,
|
||||
readings: currentReadings
|
||||
)
|
||||
marking.tooltipForInputting = currentState.tooltip
|
||||
|
@ -562,12 +613,12 @@ extension KeyHandler {
|
|||
if input.isShiftHold {
|
||||
// Shift + left
|
||||
if currentState.cursorIndex > 0 {
|
||||
let previousPosition = (currentState.composingBuffer as NSString).previousUtf16Position(
|
||||
for: Int(currentState.cursorIndex))
|
||||
let previousPosition = currentState.composingBuffer.utf16PreviousPosition(
|
||||
for: currentState.cursorIndex)
|
||||
let marking: InputState.Marking! = InputState.Marking(
|
||||
composingBuffer: currentState.composingBuffer,
|
||||
cursorIndex: currentState.cursorIndex,
|
||||
markerIndex: UInt(previousPosition),
|
||||
markerIndex: previousPosition,
|
||||
readings: currentReadings
|
||||
)
|
||||
marking.tooltipForInputting = currentState.tooltip
|
||||
|
|
|
@ -26,50 +26,47 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
import Cocoa
|
||||
|
||||
extension NSString {
|
||||
/// Converts the index in an NSString to the index in a Swift string.
|
||||
/// Shiki's Notes: The cursor index in the IMK inline composition buffer
|
||||
/// still uses UTF16 index measurements. This means that any attempt of
|
||||
/// using Swift native UTF8 handlings to replace Zonble's NSString (or
|
||||
/// .utf16) handlings below will still result in unavoidable necessities
|
||||
/// of solving the UTF16->UTF8 conversions in another approach. Therefore,
|
||||
/// I strongly advise against any attempt of such until the day that IMK is
|
||||
/// capable of handling the cursor index in its inline composition buffer using
|
||||
/// UTF8 measurements.
|
||||
|
||||
extension String {
|
||||
/// Converts the index in an NSString or .utf16 to the index in a Swift string.
|
||||
///
|
||||
/// An Emoji might be compose by more than one UTF-16 code points, however
|
||||
/// An Emoji might be compose by more than one UTF-16 code points. However,
|
||||
/// the length of an NSString is only the sum of the UTF-16 code points. It
|
||||
/// causes that the NSString and Swift string representation of the same
|
||||
/// string have different lengths once the string contains such Emoji. The
|
||||
/// method helps to find the index in a Swift string by passing the index
|
||||
/// in an NSString.
|
||||
public func characterIndex(from utf16Index: Int) -> (Int, String) {
|
||||
let string = (self as String)
|
||||
/// in an NSString (or .utf16).
|
||||
public func charIndexLiteral(from utf16Index: Int) -> Int {
|
||||
var length = 0
|
||||
for (i, character) in string.enumerated() {
|
||||
for (i, character) in self.enumerated() {
|
||||
length += character.utf16.count
|
||||
if length > utf16Index {
|
||||
return (i, string)
|
||||
return (i)
|
||||
}
|
||||
}
|
||||
return (string.count, string)
|
||||
return count
|
||||
}
|
||||
|
||||
public func nextUtf16Position(for index: Int) -> Int {
|
||||
var (fixedIndex, string) = characterIndex(from: index)
|
||||
if fixedIndex < string.count {
|
||||
fixedIndex += 1
|
||||
}
|
||||
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
|
||||
public func utf16NextPosition(for index: Int) -> Int {
|
||||
let fixedIndex = min(charIndexLiteral(from: index) + 1, count)
|
||||
return self[..<self.index(startIndex, offsetBy: fixedIndex)].utf16.count
|
||||
}
|
||||
|
||||
public func previousUtf16Position(for index: Int) -> Int {
|
||||
var (fixedIndex, string) = characterIndex(from: index)
|
||||
if fixedIndex > 0 {
|
||||
fixedIndex -= 1
|
||||
}
|
||||
return string[..<string.index(string.startIndex, offsetBy: fixedIndex)].utf16.count
|
||||
public func utf16PreviousPosition(for index: Int) -> Int {
|
||||
let fixedIndex = max(charIndexLiteral(from: index) - 1, 0)
|
||||
return self[..<self.index(startIndex, offsetBy: fixedIndex)].utf16.count
|
||||
}
|
||||
|
||||
public var count: Int {
|
||||
(self as String).count
|
||||
}
|
||||
|
||||
public func split() -> [NSString] {
|
||||
Array(self as String).map {
|
||||
NSString(string: String($0))
|
||||
}
|
||||
func utf16SubString(with r: Range<Int>) -> String {
|
||||
let arr = Array(self.utf16)[r].map { $0 }
|
||||
return String(utf16CodeUnits: arr, count: arr.count)
|
||||
}
|
||||
}
|
|
@ -670,7 +670,7 @@ public struct Tekkon {
|
|||
default: break
|
||||
}
|
||||
}
|
||||
case "ㄑ":
|
||||
case "ㄔ":
|
||||
if intonation.isEmpty {
|
||||
switch incomingPhonabet {
|
||||
case "ㄧ": consonant = "ㄑ" // ㄐㄧ
|
||||
|
@ -718,7 +718,6 @@ public struct Tekkon {
|
|||
consonant.selfReplace("ㄑ", "ㄔ")
|
||||
consonant.selfReplace("ㄒ", "ㄕ")
|
||||
}
|
||||
if vowel == "ㄜ", semivowel.isEmpty { consonant.selfReplace("ㄑ", "ㄔ") }
|
||||
if consonant == "ㄏ", semivowel.isEmpty, vowel.isEmpty {
|
||||
consonant = ""
|
||||
vowel = "ㄛ"
|
||||
|
@ -1289,7 +1288,7 @@ public struct Tekkon {
|
|||
/// 這裡提前對複音按鍵做處理,然後再用程式判斷介母類型、據此判斷是否需要做複音切換。
|
||||
static let mapHsuStaticKeys: [String: String] = [
|
||||
"a": "ㄘ", "b": "ㄅ", "c": "ㄒ", "d": "ㄉ", "e": "ㄧ", "f": "ㄈ", "g": "ㄍ", "h": "ㄏ", "i": "ㄞ", "j": "ㄐ", "k": "ㄎ",
|
||||
"l": "ㄌ", "m": "ㄇ", "n": "ㄋ", "o": "ㄡ", "p": "ㄆ", "r": "ㄖ", "s": "ㄙ", "t": "ㄊ", "u": "ㄩ", "v": "ㄑ", "w": "ㄠ",
|
||||
"l": "ㄌ", "m": "ㄇ", "n": "ㄋ", "o": "ㄡ", "p": "ㄆ", "r": "ㄖ", "s": "ㄙ", "t": "ㄊ", "u": "ㄩ", "v": "ㄔ", "w": "ㄠ",
|
||||
"x": "ㄨ", "y": "ㄚ", "z": "ㄗ", " ": " ",
|
||||
]
|
||||
|
||||
|
|
|
@ -355,9 +355,16 @@ extension RangeReplaceableCollection where Element: Hashable {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Error Extension
|
||||
// MARK: - String Tildes Expansion Extension
|
||||
|
||||
extension String {
|
||||
public var expandingTildeInPath: String {
|
||||
(self as NSString).expandingTildeInPath
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - String Localized Error Extension
|
||||
|
||||
extension String: Error {}
|
||||
extension String: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
self
|
||||
|
|
|
@ -29,18 +29,13 @@ import InputMethodKit
|
|||
|
||||
private let kMinKeyLabelSize: CGFloat = 10
|
||||
|
||||
private var ctlCandidateCurrent: ctlCandidate?
|
||||
|
||||
extension ctlCandidate {
|
||||
fileprivate static let horizontal = ctlCandidateHorizontal()
|
||||
fileprivate static let vertical = ctlCandidateVertical()
|
||||
}
|
||||
private var ctlCandidateCurrent = ctlCandidateUniversal.init(.horizontal)
|
||||
|
||||
@objc(ctlInputMethod)
|
||||
class ctlInputMethod: IMKInputController {
|
||||
@objc static var areWeDeleting = false
|
||||
|
||||
private static let tooltipController = TooltipController()
|
||||
static let tooltipController = TooltipController()
|
||||
|
||||
// MARK: -
|
||||
|
||||
|
@ -173,7 +168,7 @@ class ctlInputMethod: IMKInputController {
|
|||
forCharacterIndex: 0, lineHeightRectangle: &textFrame
|
||||
)
|
||||
|
||||
let useVerticalMode =
|
||||
let isTypingVertical =
|
||||
(attributes?["IMKTextOrientation"] as? NSNumber)?.intValue == 0 || false
|
||||
|
||||
if client.bundleIdentifier()
|
||||
|
@ -184,7 +179,7 @@ class ctlInputMethod: IMKInputController {
|
|||
IME.areWeUsingOurOwnPhraseEditor = false
|
||||
}
|
||||
|
||||
let input = InputSignal(event: event, isVerticalMode: useVerticalMode)
|
||||
let input = InputSignal(event: event, isVerticalTyping: isTypingVertical)
|
||||
|
||||
// 無法列印的訊號輸入,一概不作處理。
|
||||
// 這個過程不能放在 KeyHandler 內,否則不會起作用。
|
||||
|
@ -286,8 +281,8 @@ extension ctlInputMethod {
|
|||
_ = state // Stop clang-format from ruining the parameters of this function.
|
||||
currentClient = nil
|
||||
|
||||
ctlCandidateCurrent?.delegate = nil
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.delegate = nil
|
||||
ctlCandidateCurrent.visible = false
|
||||
hideTooltip()
|
||||
|
||||
if let previous = previous as? InputState.NotEmpty {
|
||||
|
@ -301,7 +296,7 @@ extension ctlInputMethod {
|
|||
|
||||
private func handle(state: InputState.Empty, previous: InputState, client: Any?) {
|
||||
_ = state // Stop clang-format from ruining the parameters of this function.
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
hideTooltip()
|
||||
|
||||
guard let client = client as? IMKTextInput else {
|
||||
|
@ -324,7 +319,7 @@ extension ctlInputMethod {
|
|||
) {
|
||||
_ = state // Stop clang-format from ruining the parameters of this function.
|
||||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
hideTooltip()
|
||||
|
||||
guard let client = client as? IMKTextInput else {
|
||||
|
@ -339,7 +334,7 @@ extension ctlInputMethod {
|
|||
|
||||
private func handle(state: InputState.Committing, previous: InputState, client: Any?) {
|
||||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
hideTooltip()
|
||||
|
||||
guard let client = client as? IMKTextInput else {
|
||||
|
@ -358,7 +353,7 @@ extension ctlInputMethod {
|
|||
|
||||
private func handle(state: InputState.Inputting, previous: InputState, client: Any?) {
|
||||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
hideTooltip()
|
||||
|
||||
guard let client = client as? IMKTextInput else {
|
||||
|
@ -373,7 +368,7 @@ extension ctlInputMethod {
|
|||
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
|
||||
// i.e. the client app needs to take care of where to put this composing buffer
|
||||
client.setMarkedText(
|
||||
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
|
||||
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
|
||||
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
|
||||
)
|
||||
if !state.tooltip.isEmpty {
|
||||
|
@ -386,7 +381,7 @@ extension ctlInputMethod {
|
|||
|
||||
private func handle(state: InputState.Marking, previous: InputState, client: Any?) {
|
||||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
guard let client = client as? IMKTextInput else {
|
||||
hideTooltip()
|
||||
return
|
||||
|
@ -395,7 +390,7 @@ extension ctlInputMethod {
|
|||
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
|
||||
// i.e. the client app needs to take care of where to put this composing buffer
|
||||
client.setMarkedText(
|
||||
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
|
||||
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
|
||||
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
|
||||
)
|
||||
|
||||
|
@ -413,14 +408,14 @@ extension ctlInputMethod {
|
|||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
hideTooltip()
|
||||
guard let client = client as? IMKTextInput else {
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
return
|
||||
}
|
||||
|
||||
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
|
||||
// i.e. the client app needs to take care of where to put this composing buffer
|
||||
client.setMarkedText(
|
||||
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
|
||||
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
|
||||
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
|
||||
)
|
||||
show(candidateWindowWith: state, client: client)
|
||||
|
@ -430,14 +425,14 @@ extension ctlInputMethod {
|
|||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
hideTooltip()
|
||||
guard let client = client as? IMKTextInput else {
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
return
|
||||
}
|
||||
|
||||
// the selection range is where the cursor is, with the length being 0 and replacement range NSNotFound,
|
||||
// i.e. the client app needs to take care of where to put this composing buffer
|
||||
client.setMarkedText(
|
||||
state.attributedString, selectionRange: NSRange(location: Int(state.cursorIndex), length: 0),
|
||||
state.attributedString, selectionRange: NSRange(location: state.cursorIndex, length: 0),
|
||||
replacementRange: NSRange(location: NSNotFound, length: NSNotFound)
|
||||
)
|
||||
show(candidateWindowWith: state, client: client)
|
||||
|
@ -447,7 +442,7 @@ extension ctlInputMethod {
|
|||
_ = previous // Stop clang-format from ruining the parameters of this function.
|
||||
hideTooltip()
|
||||
guard let client = client as? IMKTextInput else {
|
||||
ctlCandidateCurrent?.visible = false
|
||||
ctlCandidateCurrent.visible = false
|
||||
return
|
||||
}
|
||||
client.setMarkedText(
|
||||
|
@ -462,47 +457,48 @@ extension ctlInputMethod {
|
|||
|
||||
extension ctlInputMethod {
|
||||
private func show(candidateWindowWith state: InputState, client: Any!) {
|
||||
let useVerticalMode: Bool = {
|
||||
var useVerticalMode = false
|
||||
var isTypingVertical: Bool {
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return state.isTypingVertical
|
||||
} else if let state = state as? InputState.AssociatedPhrases {
|
||||
return state.isTypingVertical
|
||||
}
|
||||
return false
|
||||
}
|
||||
var isCandidateWindowVertical: Bool {
|
||||
var candidates: [String] = []
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
useVerticalMode = state.useVerticalMode
|
||||
candidates = state.candidates
|
||||
} else if let state = state as? InputState.AssociatedPhrases {
|
||||
useVerticalMode = state.useVerticalMode
|
||||
candidates = state.candidates
|
||||
}
|
||||
if useVerticalMode == true {
|
||||
return true
|
||||
}
|
||||
if isTypingVertical { return true }
|
||||
// 以上是通用情形。接下來決定橫排輸入時是否使用縱排選字窗。
|
||||
candidates.sort {
|
||||
$0.count > $1.count
|
||||
}
|
||||
if let candidateFirst = candidates.first {
|
||||
// If there is a candidate which is too long, we use the vertical
|
||||
// candidate list window automatically.
|
||||
if candidateFirst.count > 8 {
|
||||
// return true // 禁用這一項。威注音回頭會換候選窗格。
|
||||
}
|
||||
}
|
||||
// 如果是顏文字選單的話,則強行使用縱排候選字窗。
|
||||
// 有些顏文字會比較長,所以這裡用 for 判斷。
|
||||
for candidate in candidates {
|
||||
if ["顏文字", "颜文字"].contains(candidate), mgrPrefs.symbolInputEnabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}()
|
||||
// 測量每頁顯示候選字的累計總長度。如果太長的話就強制使用縱排候選字窗。
|
||||
// 範例:「屬實牛逼」(會有一大串各種各樣的「鼠食牛Beer」的 emoji)。
|
||||
let maxCandidatesPerPage = mgrPrefs.candidateKeys.count
|
||||
let firstPageCandidates = candidates[0..<min(maxCandidatesPerPage, candidates.count)]
|
||||
return firstPageCandidates.joined().count > Int(round(Double(maxCandidatesPerPage) * 1.8))
|
||||
// 上面這句如果是 true 的話,就會是縱排;反之則為橫排。
|
||||
}
|
||||
|
||||
ctlCandidateCurrent?.delegate = nil
|
||||
ctlCandidateCurrent.delegate = nil
|
||||
|
||||
if useVerticalMode {
|
||||
ctlCandidateCurrent = .vertical
|
||||
/// 下面這一段本可直接指定 currentLayout,但這樣的話翻頁按鈕位置無法精準地重新繪製。
|
||||
/// 所以只能重新初期化。壞處就是得在 ctlCandidate() 當中與 SymbolTable 控制有關的地方
|
||||
/// 新增一個空狀態請求、防止縱排與橫排選字窗同時出現。
|
||||
/// layoutCandidateView 在這裡無法起到糾正作用。
|
||||
/// 該問題徹底解決的價值並不大,直接等到 macOS 10.x 全線淘汰之後用 SwiftUI 重寫選字窗吧。
|
||||
|
||||
if isCandidateWindowVertical { // 縱排輸入時強制使用縱排選字窗
|
||||
ctlCandidateCurrent = .init(.vertical)
|
||||
} else if mgrPrefs.useHorizontalCandidateList {
|
||||
ctlCandidateCurrent = .horizontal
|
||||
ctlCandidateCurrent = .init(.horizontal)
|
||||
} else {
|
||||
ctlCandidateCurrent = .vertical
|
||||
ctlCandidateCurrent = .init(.vertical)
|
||||
}
|
||||
|
||||
// set the attributes for the candidate panel (which uses NSAttributedString)
|
||||
|
@ -530,10 +526,10 @@ extension ctlInputMethod {
|
|||
return finalReturnFont
|
||||
}
|
||||
|
||||
ctlCandidateCurrent?.keyLabelFont = labelFont(
|
||||
ctlCandidateCurrent.keyLabelFont = labelFont(
|
||||
name: mgrPrefs.candidateKeyLabelFontName, size: keyLabelSize
|
||||
)
|
||||
ctlCandidateCurrent?.candidateFont = candidateFont(
|
||||
ctlCandidateCurrent.candidateFont = candidateFont(
|
||||
name: mgrPrefs.candidateTextFontName, size: textSize
|
||||
)
|
||||
|
||||
|
@ -541,21 +537,21 @@ extension ctlInputMethod {
|
|||
let keyLabels =
|
||||
candidateKeys.count > 4 ? Array(candidateKeys) : Array(mgrPrefs.defaultCandidateKeys)
|
||||
let keyLabelSuffix = state is InputState.AssociatedPhrases ? "^" : ""
|
||||
ctlCandidateCurrent?.keyLabels = keyLabels.map {
|
||||
ctlCandidateCurrent.keyLabels = keyLabels.map {
|
||||
CandidateKeyLabel(key: String($0), displayedText: String($0) + keyLabelSuffix)
|
||||
}
|
||||
|
||||
ctlCandidateCurrent?.delegate = self
|
||||
ctlCandidateCurrent?.reloadData()
|
||||
ctlCandidateCurrent.delegate = self
|
||||
ctlCandidateCurrent.reloadData()
|
||||
currentClient = client
|
||||
|
||||
ctlCandidateCurrent?.visible = true
|
||||
ctlCandidateCurrent.visible = true
|
||||
|
||||
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
|
||||
var cursor = 0
|
||||
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
cursor = Int(state.cursorIndex)
|
||||
cursor = state.cursorIndex
|
||||
if cursor == state.composingBuffer.count, cursor != 0 {
|
||||
cursor -= 1
|
||||
}
|
||||
|
@ -568,24 +564,24 @@ extension ctlInputMethod {
|
|||
cursor -= 1
|
||||
}
|
||||
|
||||
if useVerticalMode {
|
||||
ctlCandidateCurrent?.set(
|
||||
if isTypingVertical {
|
||||
ctlCandidateCurrent.set(
|
||||
windowTopLeftPoint: NSPoint(
|
||||
x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0
|
||||
),
|
||||
bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0
|
||||
)
|
||||
} else {
|
||||
ctlCandidateCurrent?.set(
|
||||
ctlCandidateCurrent.set(
|
||||
windowTopLeftPoint: NSPoint(x: lineHeightRect.origin.x, y: lineHeightRect.origin.y - 4.0),
|
||||
bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func show(tooltip: String, composingBuffer: String, cursorIndex: UInt, client: Any!) {
|
||||
private func show(tooltip: String, composingBuffer: String, cursorIndex: Int, client: Any!) {
|
||||
var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0)
|
||||
var cursor = Int(cursorIndex)
|
||||
var cursor = cursorIndex
|
||||
if cursor == composingBuffer.count, cursor != 0 {
|
||||
cursor -= 1
|
||||
}
|
||||
|
@ -606,19 +602,13 @@ extension ctlInputMethod {
|
|||
// MARK: -
|
||||
|
||||
extension ctlInputMethod: KeyHandlerDelegate {
|
||||
func ctlCandidate(for keyHandler: KeyHandler) -> Any {
|
||||
_ = keyHandler // Stop clang-format from ruining the parameters of this function.
|
||||
return ctlCandidateCurrent ?? .vertical
|
||||
}
|
||||
func ctlCandidate() -> ctlCandidate { ctlCandidateCurrent }
|
||||
|
||||
func keyHandler(
|
||||
_ keyHandler: KeyHandler, didSelectCandidateAt index: Int,
|
||||
ctlCandidate controller: Any
|
||||
_: KeyHandler, didSelectCandidateAt index: Int,
|
||||
ctlCandidate controller: ctlCandidate
|
||||
) {
|
||||
_ = keyHandler // Stop clang-format from ruining the parameters of this function.
|
||||
if let controller = controller as? ctlCandidate {
|
||||
ctlCandidate(controller, didSelectCandidateAtIndex: UInt(index))
|
||||
}
|
||||
ctlCandidate(controller, didSelectCandidateAtIndex: index)
|
||||
}
|
||||
|
||||
func keyHandler(_ keyHandler: KeyHandler, didRequestWriteUserPhraseWith state: InputState)
|
||||
|
@ -653,38 +643,39 @@ extension ctlInputMethod: KeyHandlerDelegate {
|
|||
// MARK: -
|
||||
|
||||
extension ctlInputMethod: ctlCandidateDelegate {
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> UInt {
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> Int {
|
||||
_ = controller // Stop clang-format from ruining the parameters of this function.
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return UInt(state.candidates.count)
|
||||
return state.candidates.count
|
||||
} else if let state = state as? InputState.AssociatedPhrases {
|
||||
return UInt(state.candidates.count)
|
||||
return state.candidates.count
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt)
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
||||
-> String
|
||||
{
|
||||
_ = controller // Stop clang-format from ruining the parameters of this function.
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
return state.candidates[Int(index)]
|
||||
return state.candidates[index]
|
||||
} else if let state = state as? InputState.AssociatedPhrases {
|
||||
return state.candidates[Int(index)]
|
||||
return state.candidates[index]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: UInt) {
|
||||
func ctlCandidate(_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int) {
|
||||
_ = controller // Stop clang-format from ruining the parameters of this function.
|
||||
let client = currentClient
|
||||
|
||||
if let state = state as? InputState.SymbolTable,
|
||||
let node = state.node.children?[Int(index)]
|
||||
let node = state.node.children?[index]
|
||||
{
|
||||
if let children = node.children, !children.isEmpty {
|
||||
handle(state: .Empty(), client: client) // 防止縱橫排選字窗同時出現
|
||||
handle(
|
||||
state: .SymbolTable(node: node, useVerticalMode: state.useVerticalMode),
|
||||
state: .SymbolTable(node: node, isTypingVertical: state.isTypingVertical),
|
||||
client: currentClient
|
||||
)
|
||||
} else {
|
||||
|
@ -695,7 +686,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
}
|
||||
|
||||
if let state = state as? InputState.ChoosingCandidate {
|
||||
let selectedValue = state.candidates[Int(index)]
|
||||
let selectedValue = state.candidates[index]
|
||||
keyHandler.fixNode(value: selectedValue, respectCursorPushing: true)
|
||||
|
||||
let inputting = keyHandler.buildInputtingState
|
||||
|
@ -706,7 +697,7 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
handle(state: .Committing(poppedText: composingBuffer), client: client)
|
||||
if mgrPrefs.associatedPhrasesEnabled,
|
||||
let associatePhrases = keyHandler.buildAssociatePhraseState(
|
||||
withKey: composingBuffer, useVerticalMode: state.useVerticalMode
|
||||
withKey: composingBuffer, isTypingVertical: state.isTypingVertical
|
||||
), !associatePhrases.candidates.isEmpty
|
||||
{
|
||||
handle(state: associatePhrases, client: client)
|
||||
|
@ -720,11 +711,11 @@ extension ctlInputMethod: ctlCandidateDelegate {
|
|||
}
|
||||
|
||||
if let state = state as? InputState.AssociatedPhrases {
|
||||
let selectedValue = state.candidates[Int(index)]
|
||||
let selectedValue = state.candidates[index]
|
||||
handle(state: .Committing(poppedText: selectedValue), client: currentClient)
|
||||
if mgrPrefs.associatedPhrasesEnabled,
|
||||
let associatePhrases = keyHandler.buildAssociatePhraseState(
|
||||
withKey: selectedValue, useVerticalMode: state.useVerticalMode
|
||||
withKey: selectedValue, isTypingVertical: state.isTypingVertical
|
||||
), !associatePhrases.candidates.isEmpty
|
||||
{
|
||||
handle(state: associatePhrases, client: client)
|
||||
|
|
|
@ -50,6 +50,7 @@ struct UserDef {
|
|||
static let kEscToCleanInputBuffer = "EscToCleanInputBuffer"
|
||||
static let kSpecifyShiftTabKeyBehavior = "SpecifyShiftTabKeyBehavior"
|
||||
static let kSpecifyShiftSpaceKeyBehavior = "SpecifyShiftSpaceKeyBehavior"
|
||||
static let kAllowBoostingSingleKanjiAsUserPhrase = "AllowBoostingSingleKanjiAsUserPhrase"
|
||||
static let kUseSCPCTypingMode = "UseSCPCTypingMode"
|
||||
static let kMaxCandidateLength = "MaxCandidateLength"
|
||||
static let kShouldNotFartInLieuOfBeep = "ShouldNotFartInLieuOfBeep"
|
||||
|
@ -266,6 +267,9 @@ public enum mgrPrefs {
|
|||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.inlineDumpPinyinInLieuOfZhuyin, forKey: UserDef.kInlineDumpPinyinInLieuOfZhuyin
|
||||
)
|
||||
UserDefaults.standard.setDefault(
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase, forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase
|
||||
)
|
||||
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeySCPC, forKey: UserDef.kUsingHotKeySCPC)
|
||||
UserDefaults.standard.setDefault(mgrPrefs.usingHotKeyAssociates, forKey: UserDef.kUsingHotKeyAssociates)
|
||||
|
@ -337,6 +341,13 @@ public enum mgrPrefs {
|
|||
@UserDefault(key: UserDef.kChooseCandidateUsingSpace, defaultValue: true)
|
||||
static var chooseCandidateUsingSpace: Bool
|
||||
|
||||
@UserDefault(key: UserDef.kAllowBoostingSingleKanjiAsUserPhrase, defaultValue: false)
|
||||
static var allowBoostingSingleKanjiAsUserPhrase: Bool
|
||||
|
||||
static var minCandidateLength: Int {
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase ? 1 : 2
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kUseSCPCTypingMode, defaultValue: false)
|
||||
static var useSCPCTypingMode: Bool
|
||||
|
||||
|
@ -346,7 +357,7 @@ public enum mgrPrefs {
|
|||
return useSCPCTypingMode
|
||||
}
|
||||
|
||||
@UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: kDefaultComposingBufferSize * 2)
|
||||
@UserDefault(key: UserDef.kMaxCandidateLength, defaultValue: 10)
|
||||
static var maxCandidateLength: Int
|
||||
|
||||
@UserDefault(key: UserDef.kShouldNotFartInLieuOfBeep, defaultValue: true)
|
||||
|
|
|
@ -145,7 +145,7 @@ extension vChewing {
|
|||
for netaSet in arrRangeRecords {
|
||||
let strNetaSet = String(decoding: netaSet, as: UTF8.self)
|
||||
let neta = Array(strNetaSet.split(separator: " ").reversed())
|
||||
let theValue: String = String(neta[0])
|
||||
let theValue: String = .init(neta[0])
|
||||
let kvPair = Megrez.KeyValuePair(key: key, value: theValue)
|
||||
var theScore = defaultScore
|
||||
if neta.count >= 2, !shouldForceDefaultScore {
|
||||
|
|
|
@ -357,10 +357,9 @@ enum mgrLangModel {
|
|||
|
||||
static func dataFolderPath(isDefaultFolder: Bool) -> String {
|
||||
let appSupportPath = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].path
|
||||
var userDictPathSpecified = (mgrPrefs.userDataFolderSpecified as NSString).expandingTildeInPath
|
||||
var userDictPathSpecified = mgrPrefs.userDataFolderSpecified.expandingTildeInPath
|
||||
var userDictPathDefault =
|
||||
(URL(fileURLWithPath: appSupportPath).appendingPathComponent("vChewing").path as NSString)
|
||||
.expandingTildeInPath
|
||||
URL(fileURLWithPath: appSupportPath).appendingPathComponent("vChewing").path.expandingTildeInPath
|
||||
|
||||
userDictPathDefault.ensureTrailingSlash()
|
||||
userDictPathSpecified.ensureTrailingSlash()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CFBundleName = "vChewing";
|
||||
CFBundleDisplayName = "vChewing";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHT" = "vChewing-CHT";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHS" = "vChewing-CHS";
|
||||
CFEULAContent = "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n2. No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements above.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n";
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
"Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…";
|
||||
"Choose your desired user data folder." = "Choose your desired user data folder.";
|
||||
"Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\".";
|
||||
"Cursor is to the rear of \"%@\"." = "Cursor is to the rear of \"%@\".";
|
||||
"Cursor is in front of \"%@\"." = "Cursor is in front of \"%@\".";
|
||||
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
|
||||
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
|
||||
"Core Dict loading complete." = "Core Dict loading complete.";
|
||||
|
@ -80,6 +82,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"An accomodation for elder computer users." = "An accomodation for elder computer users.";
|
||||
"Apple ABC (equivalent to English US)" = "Apple ABC (equivalent to English US)";
|
||||
"Apple Chewing - Dachen" = "Apple Chewing - Dachen";
|
||||
|
|
|
@ -106,7 +106,7 @@
|
|||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.</string>
|
||||
<string>© 2021-2022 vChewing Project.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CFBundleName = "vChewing";
|
||||
CFBundleDisplayName = "vChewing";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHT" = "vChewing-CHT";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHS" = "vChewing-CHS";
|
||||
CFEULAContent = "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\n1. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n2. No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements above.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n";
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
"Edit User Symbol & Emoji Data…" = "Edit User Symbol & Emoji Data…";
|
||||
"Choose your desired user data folder." = "Choose your desired user data folder.";
|
||||
"Cursor is between \"%@\" and \"%@\"." = "Cursor is between \"%@\" and \"%@\".";
|
||||
"Cursor is to the rear of \"%@\"." = "Cursor is to the rear of \"%@\".";
|
||||
"Cursor is in front of \"%@\"." = "Cursor is in front of \"%@\".";
|
||||
"Loading CHS Core Dict..." = "Loading CHS Core Dict...";
|
||||
"Loading CHT Core Dict..." = "Loading CHT Core Dict...";
|
||||
"Core Dict loading complete." = "Core Dict loading complete.";
|
||||
|
@ -80,6 +82,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"An accomodation for elder computer users." = "An accomodation for elder computer users.";
|
||||
"Apple ABC (equivalent to English US)" = "Apple ABC (equivalent to English US)";
|
||||
"Apple Chewing - Dachen" = "Apple Chewing - Dachen";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CFBundleName = "威注音";
|
||||
CFBundleDisplayName = "威注音";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHT" = "繁体威注音";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHS" = "簡体威注音";
|
||||
CFEULAContent = "以下に定める条件に従い、本ソフトウェアおよび関連文書のファイル(以下「ソフトウェア」)の複製を取得するすべての人に対し、ソフトウェアを無制限に扱うことを無償で許可します。これには、ソフトウェアの複製を使用、複写、変更、結合、掲載、頒布、サブライセンス、および/または販売する権利、およびソフトウェアを提供する相手に同じことを許可する権利も無制限に含まれます。\n\nイ)上記の著作権表示および本許諾表示を、ソフトウェアのすべての複製または重要な部分に記載するものとします。\n\nロ)上記の通知要件を満たすために必要な場合を除き、コントリビューターの商号、商標、サービスマーク、または製品名を使用するための商標ライセンスは付与されていません。\n\nソフトウェアは「現状のまま」で、明示であるか暗黙であるかを問わず、何らの保証もなく提供されます。ここでいう保証とは、商品性、特定の目的への適合性、および権利非侵害についての保証も含みますが、それに限定されるものではありません。\n作者または著作権者は、契約行為、不法行為、またはそれ以外であろうと、ソフトウェアに起因または関連し、あるいはソフトウェアの使用またはその他の扱いによって生じる一切の請求、損害、その他の義務について何らの責任も負わないものとします。";
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
"Edit User Symbol & Emoji Data…" = "ユーザー符号&絵文字辞書を編集…";
|
||||
"Choose your desired user data folder." = "欲しがるユーザー辞書フォルダをお選びください。";
|
||||
"Cursor is between \"%@\" and \"%@\"." = "カーソルは「%@」と「%@」に間れ。";
|
||||
"Cursor is to the rear of \"%@\"." = "カーソルは「%@」の後ろに。";
|
||||
"Cursor is in front of \"%@\"." = "カーソルは「%@」の前に。";
|
||||
"Loading CHS Core Dict..." = "簡体中国語核心辞書読込中…";
|
||||
"Loading CHT Core Dict..." = "繁体中国語核心辞書読込中…";
|
||||
"Core Dict loading complete." = "核心辞書読込完了";
|
||||
|
@ -80,6 +82,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)Space:";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "即排除/即最優先にできる候補の文字数の最低限は1字とする";
|
||||
"An accomodation for elder computer users." = "年配なるユーザーのために提供した機能である。";
|
||||
"Apple ABC (equivalent to English US)" = "Apple ABC (Apple U.S. キーボードと同じ)";
|
||||
"Apple Chewing - Dachen" = "Apple 大千注音キーボード";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CFBundleName = "威注音";
|
||||
CFBundleDisplayName = "威注音";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHT" = "威注音-繁";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHS" = "威注音-简";
|
||||
CFEULAContent = "软件之著作权利人依此麻理授权条款,将其对于软件之著作权利授权释出,只须使用者践履以下二项麻理授权条款叙明之义务性规定,其即享有对此软件程式及其相关说明文档自由不受限制地进行利用之权利,范围包括「使用、重制、修改、合并、出版、散布、再授权、及贩售程式重制作品」等诸多方面之应用,而散布程式之人、更可将上述权利传递予其后收受程式之后手,倘若其后收受程式之人亦服膺以下二项麻理授权条款之义务性规定,则其对程式亦享有与前手运用范围相同之同一权利。\n\n甲、散布此一软件程式者,须将本条款其上之「著作权声明」及以下之「免责声明」内嵌于软件程式及其重制作品之实体之中。\n\n乙、敝授权合约不提供对「贡献者」之商品名称、商标、服务标志或产品名称之商标许可,除非用以满足履行上文所述义务之必要。\n\n因麻理软件程式之授权模式乃是无偿提供,是以在现行法律之架构下可以主张合理之免除担保责任。麻理软件之著作权人或任何之后续散布者,对于其所散布之麻理软件程式皆不负任何形式上实质上之担保责任,明示亦或隐喻、商业利用性亦或特定目之使用性,这些均不在保障之列。利用麻理软件程式之所有风险均由使用者自行担负。假如所使用之麻理程式发生缺陷性问题,使用者需自行担负修正、改正及必要之服务支出。麻理软件程式之著作权人不负任何形式上实质上之担保责任,无论任何一般之、特殊之、偶发之、因果关系式之损害,或是麻理软件程式之不适用性,均须由使用者自行负担。\n";
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
"Edit User Symbol & Emoji Data…" = "编辑自订符号&绘文字资料…";
|
||||
"Choose your desired user data folder." = "请选择您想指定的使用者语汇档案目录。";
|
||||
"Cursor is between \"%@\" and \"%@\"." = "游标介于「%@」与「%@」之间。";
|
||||
"Cursor is to the rear of \"%@\"." = "游标在「%@」的后方。";
|
||||
"Cursor is in front of \"%@\"." = "游标在「%@」的前方。.";
|
||||
"Loading CHS Core Dict..." = "载入简体中文核心辞典…";
|
||||
"Loading CHT Core Dict..." = "载入繁体中文核心辞典…";
|
||||
"Core Dict loading complete." = "核心辞典载入完毕";
|
||||
|
@ -80,6 +82,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)空格键:";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "将可以就地升权/排除的候选字词的最短词长设为单个汉字";
|
||||
"An accomodation for elder computer users." = "针对年长使用者的习惯而提供。";
|
||||
"Apple ABC (equivalent to English US)" = "Apple ABC (与 Apple 美规键盘等价)";
|
||||
"Apple Chewing - Dachen" = "Apple 大千注音键盘排列";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
CFBundleName = "威注音";
|
||||
CFBundleDisplayName = "威注音";
|
||||
NSHumanReadableCopyright = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
NSHumanReadableCopyright = "© 2021-2022 vChewing Project.";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHT" = "威注音-繁";
|
||||
"org.atelierInmu.inputmethod.vChewing.IMECHS" = "威注音-簡";
|
||||
CFEULAContent = "軟體之著作權利人依此麻理授權條款,將其對於軟體之著作權利授權釋出,只須使用者踐履以下二項麻理授權條款敘明之義務性規定,其即享有對此軟體程式及其相關說明文檔自由不受限制地進行利用之權利,範圍包括「使用、重製、修改、合併、出版、散布、再授權、及販售程式重製作品」等諸多方面之應用,而散布程式之人、更可將上述權利傳遞予其後收受程式之後手,倘若其後收受程式之人亦服膺以下二項麻理授權條款之義務性規定,則其對程式亦享有與前手運用範圍相同之同一權利。\n\n甲、散布此一軟體程式者,須將本條款其上之「著作權聲明」及以下之「免責聲明」內嵌於軟體程式及其重製作品之實體之中。\n\n乙、敝授權合約不提供對「貢獻者」之商品名稱、商標、服務標誌或產品名稱之商標許可,除非用以滿足履行上文所述義務之必要。\n\n因麻理軟體程式之授權模式乃是無償提供,是以在現行法律之架構下可以主張合理之免除擔保責任。麻理軟體之著作權人或任何之後續散布者,對於其所散布之麻理軟體程式皆不負任何形式上實質上之擔保責任,明示亦或隱喻、商業利用性亦或特定目之使用性,這些均不在保障之列。利用麻理軟體程式之所有風險均由使用者自行擔負。假如所使用之麻理程式發生缺陷性問題,使用者需自行擔負修正、改正及必要之服務支出。麻理軟體程式之著作權人不負任何形式上實質上之擔保責任,無論任何一般之、特殊之、偶發之、因果關係式之損害,或是麻理軟體程式之不適用性,均須由使用者自行負擔。\n";
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
"Edit User Symbol & Emoji Data…" = "編輯自訂符號&繪文字資料…";
|
||||
"Choose your desired user data folder." = "請選擇您想指定的使用者語彙檔案目錄。";
|
||||
"Cursor is between \"%@\" and \"%@\"." = "游標介於「%@」與「%@」之間。";
|
||||
"Cursor is to the rear of \"%@\"." = "游標在「%@」的後方。";
|
||||
"Cursor is in front of \"%@\"." = "游標在「%@」的前方。.";
|
||||
"Loading CHS Core Dict..." = "載入簡體中文核心辭典…";
|
||||
"Loading CHT Core Dict..." = "載入繁體中文核心辭典…";
|
||||
"Core Dict loading complete." = "核心辭典載入完畢";
|
||||
|
@ -80,6 +82,7 @@
|
|||
|
||||
// SwiftUI Preferences
|
||||
"(Shift+)Space:" = "(Shift+)空格鍵:";
|
||||
"Allow boosting / excluding a candidate of single kanji" = "將可以就地升權/排除的候選字詞的最短詞長設為單個漢字";
|
||||
"An accomodation for elder computer users." = "針對年長使用者的習慣而提供。";
|
||||
"Apple ABC (equivalent to English US)" = "Apple ABC (與 Apple 美規鍵盤等價)";
|
||||
"Apple Chewing - Dachen" = "Apple 大千注音鍵盤佈局";
|
||||
|
|
|
@ -38,22 +38,27 @@ public class CandidateKeyLabel: NSObject {
|
|||
}
|
||||
|
||||
public protocol ctlCandidateDelegate: AnyObject {
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> UInt
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: UInt)
|
||||
func candidateCountForController(_ controller: ctlCandidate) -> Int
|
||||
func ctlCandidate(_ controller: ctlCandidate, candidateAtIndex index: Int)
|
||||
-> String
|
||||
func ctlCandidate(
|
||||
_ controller: ctlCandidate, didSelectCandidateAtIndex index: UInt
|
||||
_ controller: ctlCandidate, didSelectCandidateAtIndex index: Int
|
||||
)
|
||||
}
|
||||
|
||||
public class ctlCandidate: NSWindowController {
|
||||
public enum Layout {
|
||||
case horizontal
|
||||
case vertical
|
||||
}
|
||||
public var currentLayout: Layout = .horizontal
|
||||
public weak var delegate: ctlCandidateDelegate? {
|
||||
didSet {
|
||||
reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
public var selectedCandidateIndex: UInt = .max
|
||||
public var selectedCandidateIndex: Int = .max
|
||||
public var visible: Bool = false {
|
||||
didSet {
|
||||
NSObject.cancelPreviousPerformRequests(withTarget: self)
|
||||
|
@ -108,8 +113,8 @@ public class ctlCandidate: NSWindowController {
|
|||
false
|
||||
}
|
||||
|
||||
public func candidateIndexAtKeyLabelIndex(_: UInt) -> UInt {
|
||||
UInt.max
|
||||
public func candidateIndexAtKeyLabelIndex(_: Int) -> Int {
|
||||
Int.max
|
||||
}
|
||||
|
||||
/// Sets the location of the candidate window.
|
||||
|
|
|
@ -1,473 +0,0 @@
|
|||
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
|
||||
// All possible vChewing-specific modifications are of:
|
||||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
2. No trademark license is granted to use the trade names, trademarks, service
|
||||
marks, or product names of Contributor, except as required to fulfill notice
|
||||
requirements above.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
private class HorizontalCandidateView: NSView {
|
||||
var highlightedIndex: UInt = 0
|
||||
var action: Selector?
|
||||
weak var target: AnyObject?
|
||||
|
||||
private var keyLabels: [String] = []
|
||||
private var displayedCandidates: [String] = []
|
||||
private var dispCandidatesWithLabels: [String] = []
|
||||
private var keyLabelHeight: CGFloat = 0
|
||||
private var keyLabelWidth: CGFloat = 0
|
||||
private var candidateTextHeight: CGFloat = 0
|
||||
private var cellPadding: CGFloat = 0
|
||||
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var elementWidths: [CGFloat] = []
|
||||
private var trackingHighlightedIndex: UInt = .max
|
||||
|
||||
override var isFlipped: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var sizeForView: NSSize {
|
||||
var result = NSSize.zero
|
||||
|
||||
if !elementWidths.isEmpty {
|
||||
result.width = elementWidths.reduce(0, +) + CGFloat(elementWidths.count)
|
||||
result.height = candidateTextHeight + cellPadding
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@objc(setKeyLabels:displayedCandidates:)
|
||||
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
|
||||
let count = min(labels.count, candidates.count)
|
||||
keyLabels = Array(labels[0..<count])
|
||||
displayedCandidates = Array(candidates[0..<count])
|
||||
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
|
||||
|
||||
var newWidths = [CGFloat]()
|
||||
let baseSize = NSSize(width: 10240.0, height: 10240.0)
|
||||
for index in 0..<count {
|
||||
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
|
||||
with: baseSize, options: .usesLineFragmentOrigin,
|
||||
attributes: candidateWithLabelAttrDict
|
||||
)
|
||||
var cellWidth = rctCandidate.size.width + cellPadding
|
||||
let cellHeight = rctCandidate.size.height + cellPadding
|
||||
if cellWidth < cellHeight * 1.35 {
|
||||
cellWidth = cellHeight * 1.35
|
||||
}
|
||||
newWidths.append(round(cellWidth))
|
||||
}
|
||||
elementWidths = newWidths
|
||||
}
|
||||
|
||||
@objc(setKeyLabelFont:candidateFont:)
|
||||
func set(keyLabelFont labelFont: NSFont, candidateFont: NSFont) {
|
||||
let paraStyle = NSMutableParagraphStyle()
|
||||
paraStyle.setParagraphStyle(NSParagraphStyle.default)
|
||||
paraStyle.alignment = .center
|
||||
|
||||
candidateWithLabelAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // We still need this dummy section to make sure that…
|
||||
// …the space occupations of the candidates are correct.
|
||||
|
||||
keyLabelAttrDict = [
|
||||
.font: labelFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.verticalGlyphForm: true as AnyObject,
|
||||
.foregroundColor: NSColor.secondaryLabelColor,
|
||||
] // Candidate phrase text color
|
||||
candidateAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // Candidate index text color
|
||||
let labelFontSize = labelFont.pointSize
|
||||
let candidateFontSize = candidateFont.pointSize
|
||||
let biggestSize = max(labelFontSize, candidateFontSize)
|
||||
keyLabelWidth = ceil(labelFontSize)
|
||||
keyLabelHeight = ceil(labelFontSize * 2)
|
||||
candidateTextHeight = ceil(candidateFontSize * 1.20)
|
||||
cellPadding = ceil(biggestSize / 4.0) * 2
|
||||
}
|
||||
|
||||
override func draw(_: NSRect) {
|
||||
let bounds = bounds
|
||||
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
|
||||
NSBezierPath.fill(bounds)
|
||||
|
||||
NSColor.systemGray.withAlphaComponent(0.75).setStroke()
|
||||
|
||||
NSBezierPath.strokeLine(
|
||||
from: NSPoint(x: bounds.size.width, y: 0.0),
|
||||
to: NSPoint(x: bounds.size.width, y: bounds.size.height)
|
||||
)
|
||||
|
||||
var accuWidth: CGFloat = 0
|
||||
for (index, elementWidth) in elementWidths.enumerated() {
|
||||
let currentWidth = elementWidth
|
||||
let rctCandidateArea = NSRect(
|
||||
x: accuWidth, y: 0.0, width: currentWidth + 1.0,
|
||||
height: candidateTextHeight + cellPadding
|
||||
)
|
||||
let rctLabel = NSRect(
|
||||
x: accuWidth + cellPadding / 2 - 1, y: cellPadding / 2, width: keyLabelWidth,
|
||||
height: keyLabelHeight * 2.0
|
||||
)
|
||||
let rctCandidatePhrase = NSRect(
|
||||
x: accuWidth + keyLabelWidth - 1, y: cellPadding / 2 - 1,
|
||||
width: currentWidth - keyLabelWidth,
|
||||
height: candidateTextHeight
|
||||
)
|
||||
|
||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||
var activeCandidateAttr = candidateAttrDict
|
||||
if index == highlightedIndex {
|
||||
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
|
||||
// The background color of the highlightened candidate
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
NSColor.systemRed.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
case InputMode.imeModeCHT:
|
||||
NSColor.systemBlue.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
default:
|
||||
NSColor.alternateSelectedControlColor.setFill()
|
||||
}
|
||||
// Highlightened index text color
|
||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
.withAlphaComponent(0.84)
|
||||
// Highlightened phrase text color
|
||||
activeCandidateAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
} else {
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
}
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject
|
||||
}
|
||||
case InputMode.imeModeCHT:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
NSBezierPath.fill(rctCandidateArea)
|
||||
(keyLabels[index] as NSString).draw(
|
||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||
)
|
||||
(displayedCandidates[index] as NSString).draw(
|
||||
in: rctCandidatePhrase, withAttributes: activeCandidateAttr
|
||||
)
|
||||
accuWidth += currentWidth + 1.0
|
||||
}
|
||||
}
|
||||
|
||||
private func findHitIndex(event: NSEvent) -> UInt? {
|
||||
let location = convert(event.locationInWindow, to: nil)
|
||||
if !bounds.contains(location) {
|
||||
return nil
|
||||
}
|
||||
var accuWidth: CGFloat = 0.0
|
||||
for (index, elementWidth) in elementWidths.enumerated() {
|
||||
let currentWidth = elementWidth
|
||||
|
||||
if location.x >= accuWidth, location.x <= accuWidth + currentWidth {
|
||||
return UInt(index)
|
||||
}
|
||||
accuWidth += currentWidth + 1.0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
trackingHighlightedIndex = highlightedIndex
|
||||
guard let newIndex = findHitIndex(event: event) else {
|
||||
return
|
||||
}
|
||||
highlightedIndex = newIndex
|
||||
setNeedsDisplay(bounds)
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
guard let newIndex = findHitIndex(event: event) else {
|
||||
return
|
||||
}
|
||||
var triggerAction = false
|
||||
if newIndex == highlightedIndex {
|
||||
triggerAction = true
|
||||
} else {
|
||||
highlightedIndex = trackingHighlightedIndex
|
||||
}
|
||||
|
||||
trackingHighlightedIndex = 0
|
||||
setNeedsDisplay(bounds)
|
||||
if triggerAction {
|
||||
if let target = target as? NSObject, let action = action {
|
||||
target.perform(action, with: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ctlCandidateHorizontal: ctlCandidate {
|
||||
private var candidateView: HorizontalCandidateView
|
||||
private var prevPageButton: NSButton
|
||||
private var nextPageButton: NSButton
|
||||
private var currentPageIndex: UInt = 0
|
||||
|
||||
public init() {
|
||||
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
||||
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
||||
let panel = NSPanel(
|
||||
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
|
||||
)
|
||||
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
|
||||
panel.hasShadow = true
|
||||
panel.isOpaque = false
|
||||
panel.backgroundColor = NSColor.clear
|
||||
|
||||
contentRect.origin = NSPoint.zero
|
||||
candidateView = HorizontalCandidateView(frame: contentRect)
|
||||
|
||||
candidateView.wantsLayer = true
|
||||
candidateView.layer?.borderColor =
|
||||
NSColor.selectedMenuItemTextColor.withAlphaComponent(0.10).cgColor
|
||||
candidateView.layer?.borderWidth = 1.0
|
||||
if #available(macOS 10.13, *) {
|
||||
candidateView.layer?.cornerRadius = 6.0
|
||||
}
|
||||
|
||||
panel.contentView?.addSubview(candidateView)
|
||||
|
||||
contentRect.size = NSSize(width: 20.0, height: 10.0) // Reduce the button width
|
||||
let buttonAttribute: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: 9.0)]
|
||||
|
||||
nextPageButton = NSButton(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(nextPageButton.bounds)
|
||||
nextPageButton.wantsLayer = true
|
||||
nextPageButton.layer?.masksToBounds = true
|
||||
nextPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
nextPageButton.layer?.borderWidth = 0.0
|
||||
nextPageButton.setButtonType(.momentaryLight)
|
||||
nextPageButton.bezelStyle = .disclosure
|
||||
nextPageButton.userInterfaceLayoutDirection = .leftToRight
|
||||
nextPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Next Page Arrow
|
||||
prevPageButton = NSButton(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(prevPageButton.bounds)
|
||||
prevPageButton.wantsLayer = true
|
||||
prevPageButton.layer?.masksToBounds = true
|
||||
prevPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
prevPageButton.layer?.borderWidth = 0.0
|
||||
prevPageButton.setButtonType(.momentaryLight)
|
||||
prevPageButton.bezelStyle = .disclosure
|
||||
prevPageButton.userInterfaceLayoutDirection = .rightToLeft
|
||||
prevPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Previous Page Arrow
|
||||
panel.contentView?.addSubview(nextPageButton)
|
||||
panel.contentView?.addSubview(prevPageButton)
|
||||
|
||||
super.init(window: panel)
|
||||
|
||||
candidateView.target = self
|
||||
candidateView.action = #selector(candidateViewMouseDidClick(_:))
|
||||
|
||||
nextPageButton.target = self
|
||||
nextPageButton.action = #selector(pageButtonAction(_:))
|
||||
|
||||
prevPageButton.target = self
|
||||
prevPageButton.action = #selector(pageButtonAction(_:))
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func reloadData() {
|
||||
candidateView.highlightedIndex = 0
|
||||
currentPageIndex = 0
|
||||
layoutCandidateView()
|
||||
}
|
||||
|
||||
override public func showNextPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightNextCandidate() }
|
||||
if currentPageIndex + 1 >= pageCount { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func showPreviousPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightPreviousCandidate() }
|
||||
if currentPageIndex == 0 { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightNextCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
|
||||
? 0 : selectedCandidateIndex + 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightPreviousCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex == 0)
|
||||
? delegate.candidateCountForController(self) - 1 : selectedCandidateIndex - 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt {
|
||||
guard let delegate = delegate else {
|
||||
return UInt.max
|
||||
}
|
||||
|
||||
let result = currentPageIndex * UInt(keyLabels.count) + index
|
||||
return result < delegate.candidateCountForController(self) ? result : UInt.max
|
||||
}
|
||||
|
||||
override public var selectedCandidateIndex: UInt {
|
||||
get {
|
||||
currentPageIndex * UInt(keyLabels.count) + candidateView.highlightedIndex
|
||||
}
|
||||
set {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
if newValue < delegate.candidateCountForController(self) {
|
||||
currentPageIndex = newValue / keyLabelCount
|
||||
candidateView.highlightedIndex = newValue % keyLabelCount
|
||||
layoutCandidateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ctlCandidateHorizontal {
|
||||
private var pageCount: UInt {
|
||||
guard let delegate = delegate else {
|
||||
return 0
|
||||
}
|
||||
let totalCount = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
private func layoutCandidateView() {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
|
||||
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
|
||||
var candidates = [String]()
|
||||
let count = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
|
||||
let begin = currentPageIndex * keyLabelCount
|
||||
for index in begin..<min(begin + keyLabelCount, count) {
|
||||
let candidate = delegate.ctlCandidate(self, candidateAtIndex: index)
|
||||
candidates.append(candidate)
|
||||
}
|
||||
candidateView.set(
|
||||
keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates
|
||||
)
|
||||
var newSize = candidateView.sizeForView
|
||||
var frameRect = candidateView.frame
|
||||
frameRect.size = newSize
|
||||
candidateView.frame = frameRect
|
||||
|
||||
if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow {
|
||||
var buttonRect = nextPageButton.frame
|
||||
let spacing: CGFloat = 0.0
|
||||
|
||||
buttonRect.size.height = floor(newSize.height / 2)
|
||||
|
||||
let buttonOriginY = (newSize.height - (buttonRect.size.height * 2.0 + spacing)) / 2.0
|
||||
buttonRect.origin = NSPoint(x: newSize.width, y: buttonOriginY)
|
||||
nextPageButton.frame = buttonRect
|
||||
|
||||
buttonRect.origin = NSPoint(
|
||||
x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing
|
||||
)
|
||||
prevPageButton.frame = buttonRect
|
||||
|
||||
newSize.width += 20
|
||||
nextPageButton.isHidden = false
|
||||
prevPageButton.isHidden = false
|
||||
} else {
|
||||
nextPageButton.isHidden = true
|
||||
prevPageButton.isHidden = true
|
||||
}
|
||||
|
||||
frameRect = window?.frame ?? NSRect.zero
|
||||
|
||||
let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)
|
||||
frameRect.size = newSize
|
||||
frameRect.origin = NSPoint(x: topLeftPoint.x, y: topLeftPoint.y - frameRect.size.height)
|
||||
window?.setFrame(frameRect, display: false)
|
||||
candidateView.setNeedsDisplay(candidateView.bounds)
|
||||
}
|
||||
|
||||
@objc private func pageButtonAction(_ sender: Any) {
|
||||
guard let sender = sender as? NSButton else {
|
||||
return
|
||||
}
|
||||
if sender == nextPageButton {
|
||||
_ = showNextPage()
|
||||
} else if sender == prevPageButton {
|
||||
_ = showPreviousPage()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func candidateViewMouseDidClick(_: Any) {
|
||||
delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,683 @@
|
|||
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
|
||||
// All possible vChewing-specific modifications are of:
|
||||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
2. No trademark license is granted to use the trade names, trademarks, service
|
||||
marks, or product names of Contributor, except as required to fulfill notice
|
||||
requirements above.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
// 將之前 Zonble 重寫的 Voltaire 選字窗隔的橫向版本與縱向版本合併到同一個型別實體內。
|
||||
|
||||
private class vwrCandidateUniversal: NSView {
|
||||
var highlightedIndex: Int = 0 { didSet { highlightedIndex = max(highlightedIndex, 0) } }
|
||||
var action: Selector?
|
||||
weak var target: AnyObject?
|
||||
var isVerticalLayout: Bool = false
|
||||
var fractionFontSize: CGFloat = 12.0
|
||||
|
||||
private var keyLabels: [String] = []
|
||||
private var displayedCandidates: [String] = []
|
||||
private var dispCandidatesWithLabels: [String] = []
|
||||
private var keyLabelHeight: CGFloat = 0
|
||||
private var keyLabelWidth: CGFloat = 0
|
||||
private var candidateTextHeight: CGFloat = 0
|
||||
private var cellPadding: CGFloat = 0
|
||||
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var windowWidth: CGFloat = 0 // 縱排專用
|
||||
private var elementWidths: [CGFloat] = []
|
||||
private var elementHeights: [CGFloat] = [] // 縱排專用
|
||||
private var trackingHighlightedIndex: Int = .max {
|
||||
didSet { trackingHighlightedIndex = max(trackingHighlightedIndex, 0) }
|
||||
}
|
||||
|
||||
override var isFlipped: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var sizeForView: NSSize {
|
||||
var result = NSSize.zero
|
||||
|
||||
if !elementWidths.isEmpty {
|
||||
switch isVerticalLayout {
|
||||
case true:
|
||||
result.width = windowWidth
|
||||
result.height = elementHeights.reduce(0, +)
|
||||
case false:
|
||||
result.width = elementWidths.reduce(0, +) + CGFloat(elementWidths.count)
|
||||
result.height = candidateTextHeight + cellPadding
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@objc(setKeyLabels:displayedCandidates:)
|
||||
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
|
||||
let count = min(labels.count, candidates.count)
|
||||
keyLabels = Array(labels[0..<count])
|
||||
displayedCandidates = Array(candidates[0..<count])
|
||||
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
|
||||
|
||||
var newWidths = [CGFloat]()
|
||||
var calculatedWindowWidth = CGFloat()
|
||||
var newHeights = [CGFloat]()
|
||||
let baseSize = NSSize(width: 10240.0, height: 10240.0)
|
||||
for index in 0..<count {
|
||||
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
|
||||
with: baseSize, options: .usesLineFragmentOrigin,
|
||||
attributes: candidateWithLabelAttrDict
|
||||
)
|
||||
var cellWidth = rctCandidate.size.width + cellPadding
|
||||
let cellHeight = rctCandidate.size.height + cellPadding
|
||||
switch isVerticalLayout {
|
||||
case true:
|
||||
if calculatedWindowWidth < rctCandidate.size.width {
|
||||
calculatedWindowWidth = rctCandidate.size.width + cellPadding
|
||||
}
|
||||
case false:
|
||||
if cellWidth < cellHeight * 1.35 {
|
||||
cellWidth = cellHeight * 1.35
|
||||
}
|
||||
}
|
||||
newWidths.append(round(cellWidth))
|
||||
newHeights.append(round(cellHeight)) // 縱排專用
|
||||
}
|
||||
elementWidths = newWidths
|
||||
elementHeights = newHeights
|
||||
// 縱排專用,防止窗體右側邊框粗細不一
|
||||
windowWidth = round(calculatedWindowWidth + cellPadding)
|
||||
}
|
||||
|
||||
@objc(setKeyLabelFont:candidateFont:)
|
||||
func set(keyLabelFont labelFont: NSFont, candidateFont: NSFont) {
|
||||
let paraStyle = NSMutableParagraphStyle()
|
||||
paraStyle.setParagraphStyle(NSParagraphStyle.default)
|
||||
paraStyle.alignment = isVerticalLayout ? .left : .center
|
||||
|
||||
candidateWithLabelAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // We still need this dummy section to make sure that…
|
||||
// …the space occupations of the candidates are correct.
|
||||
|
||||
keyLabelAttrDict = [
|
||||
.font: labelFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.verticalGlyphForm: true as AnyObject,
|
||||
.foregroundColor: NSColor.secondaryLabelColor,
|
||||
] // Candidate phrase text color
|
||||
candidateAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // Candidate index text color
|
||||
let labelFontSize = labelFont.pointSize
|
||||
let candidateFontSize = candidateFont.pointSize
|
||||
let biggestSize = max(labelFontSize, candidateFontSize)
|
||||
fractionFontSize = round(biggestSize * 0.75)
|
||||
keyLabelWidth = ceil(labelFontSize)
|
||||
keyLabelHeight = ceil(labelFontSize * 2)
|
||||
candidateTextHeight = ceil(candidateFontSize * 1.20)
|
||||
cellPadding = ceil(biggestSize / 4.0) * 2
|
||||
}
|
||||
|
||||
override func draw(_: NSRect) {
|
||||
let bounds = bounds
|
||||
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
|
||||
NSBezierPath.fill(bounds)
|
||||
|
||||
NSColor.systemGray.withAlphaComponent(0.75).setStroke()
|
||||
|
||||
NSBezierPath.strokeLine(
|
||||
from: NSPoint(x: bounds.size.width, y: 0.0),
|
||||
to: NSPoint(x: bounds.size.width, y: bounds.size.height)
|
||||
)
|
||||
|
||||
switch isVerticalLayout {
|
||||
case true:
|
||||
do {
|
||||
var accuHeight: CGFloat = 0
|
||||
for (index, elementHeight) in elementHeights.enumerated() {
|
||||
let currentHeight = elementHeight
|
||||
let rctCandidateArea = NSRect(
|
||||
x: 0.0, y: accuHeight, width: windowWidth, height: candidateTextHeight + cellPadding
|
||||
)
|
||||
let rctLabel = NSRect(
|
||||
x: cellPadding / 2 - 1, y: accuHeight + cellPadding / 2, width: keyLabelWidth,
|
||||
height: keyLabelHeight * 2.0
|
||||
)
|
||||
let rctCandidatePhrase = NSRect(
|
||||
x: cellPadding / 2 - 1 + keyLabelWidth, y: accuHeight + cellPadding / 2 - 1,
|
||||
width: windowWidth - keyLabelWidth, height: candidateTextHeight
|
||||
)
|
||||
|
||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||
var activeCandidateAttr = candidateAttrDict
|
||||
if index == highlightedIndex {
|
||||
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
|
||||
// The background color of the highlightened candidate
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
NSColor.systemRed.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
case InputMode.imeModeCHT:
|
||||
NSColor.systemBlue.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
default:
|
||||
NSColor.alternateSelectedControlColor.setFill()
|
||||
}
|
||||
// Highlightened index text color
|
||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
.withAlphaComponent(0.84)
|
||||
// Highlightened phrase text color
|
||||
activeCandidateAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
} else {
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
}
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject
|
||||
}
|
||||
case InputMode.imeModeCHT:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
NSBezierPath.fill(rctCandidateArea)
|
||||
(keyLabels[index] as NSString).draw(
|
||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||
)
|
||||
(displayedCandidates[index] as NSString).draw(
|
||||
in: rctCandidatePhrase, withAttributes: activeCandidateAttr
|
||||
)
|
||||
accuHeight += currentHeight
|
||||
}
|
||||
}
|
||||
case false:
|
||||
do {
|
||||
var accuWidth: CGFloat = 0
|
||||
for (index, elementWidth) in elementWidths.enumerated() {
|
||||
let currentWidth = elementWidth
|
||||
let rctCandidateArea = NSRect(
|
||||
x: accuWidth, y: 0.0, width: currentWidth + 1.0,
|
||||
height: candidateTextHeight + cellPadding
|
||||
)
|
||||
let rctLabel = NSRect(
|
||||
x: accuWidth + cellPadding / 2 - 1, y: cellPadding / 2, width: keyLabelWidth,
|
||||
height: keyLabelHeight * 2.0
|
||||
)
|
||||
let rctCandidatePhrase = NSRect(
|
||||
x: accuWidth + keyLabelWidth - 1, y: cellPadding / 2 - 1,
|
||||
width: currentWidth - keyLabelWidth,
|
||||
height: candidateTextHeight
|
||||
)
|
||||
|
||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||
var activeCandidateAttr = candidateAttrDict
|
||||
if index == highlightedIndex {
|
||||
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
|
||||
// The background color of the highlightened candidate
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
NSColor.systemRed.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
case InputMode.imeModeCHT:
|
||||
NSColor.systemBlue.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
default:
|
||||
NSColor.alternateSelectedControlColor.setFill()
|
||||
}
|
||||
// Highlightened index text color
|
||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
.withAlphaComponent(0.84)
|
||||
// Highlightened phrase text color
|
||||
activeCandidateAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
} else {
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
}
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject
|
||||
}
|
||||
case InputMode.imeModeCHT:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
NSBezierPath.fill(rctCandidateArea)
|
||||
(keyLabels[index] as NSString).draw(
|
||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||
)
|
||||
(displayedCandidates[index] as NSString).draw(
|
||||
in: rctCandidatePhrase, withAttributes: activeCandidateAttr
|
||||
)
|
||||
accuWidth += currentWidth + 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func findHitIndex(event: NSEvent) -> Int {
|
||||
let location = convert(event.locationInWindow, to: nil)
|
||||
if !bounds.contains(location) {
|
||||
return NSNotFound
|
||||
}
|
||||
switch isVerticalLayout {
|
||||
case true:
|
||||
do {
|
||||
var accuHeight: CGFloat = 0.0
|
||||
for (index, elementHeight) in elementHeights.enumerated() {
|
||||
let currentHeight = elementHeight
|
||||
|
||||
if location.y >= accuHeight, location.y <= accuHeight + currentHeight {
|
||||
return index
|
||||
}
|
||||
accuHeight += currentHeight
|
||||
}
|
||||
}
|
||||
case false:
|
||||
do {
|
||||
var accuWidth: CGFloat = 0.0
|
||||
for (index, elementWidth) in elementWidths.enumerated() {
|
||||
let currentWidth = elementWidth
|
||||
|
||||
if location.x >= accuWidth, location.x <= accuWidth + currentWidth {
|
||||
return index
|
||||
}
|
||||
accuWidth += currentWidth + 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
return NSNotFound
|
||||
}
|
||||
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
trackingHighlightedIndex = highlightedIndex
|
||||
let newIndex = findHitIndex(event: event)
|
||||
guard newIndex != NSNotFound else {
|
||||
return
|
||||
}
|
||||
highlightedIndex = newIndex
|
||||
setNeedsDisplay(bounds)
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
let newIndex = findHitIndex(event: event)
|
||||
guard newIndex != NSNotFound else {
|
||||
return
|
||||
}
|
||||
var triggerAction = false
|
||||
if newIndex == highlightedIndex {
|
||||
triggerAction = true
|
||||
} else {
|
||||
highlightedIndex = trackingHighlightedIndex
|
||||
}
|
||||
|
||||
trackingHighlightedIndex = 0
|
||||
setNeedsDisplay(bounds)
|
||||
if triggerAction {
|
||||
if let target = target as? NSObject, let action = action {
|
||||
target.perform(action, with: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ctlCandidateUniversal: ctlCandidate {
|
||||
private var candidateView: vwrCandidateUniversal
|
||||
private var prevPageButton: NSButton
|
||||
private var nextPageButton: NSButton
|
||||
private var pageCounterLabel: NSTextField
|
||||
private var currentPageIndex: Int = 0
|
||||
override public var currentLayout: Layout {
|
||||
get { candidateView.isVerticalLayout ? .vertical : .horizontal }
|
||||
set {
|
||||
switch newValue {
|
||||
case .vertical: candidateView.isVerticalLayout = true
|
||||
case .horizontal: candidateView.isVerticalLayout = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ layout: Layout = .horizontal) {
|
||||
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
||||
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
||||
let panel = NSPanel(
|
||||
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
|
||||
)
|
||||
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
|
||||
panel.hasShadow = true
|
||||
panel.isOpaque = false
|
||||
panel.backgroundColor = NSColor.clear
|
||||
|
||||
contentRect.origin = NSPoint.zero
|
||||
candidateView = vwrCandidateUniversal(frame: contentRect)
|
||||
|
||||
candidateView.wantsLayer = true
|
||||
candidateView.layer?.borderColor =
|
||||
NSColor.selectedMenuItemTextColor.withAlphaComponent(0.10).cgColor
|
||||
candidateView.layer?.borderWidth = 1.0
|
||||
if #available(macOS 10.13, *) {
|
||||
candidateView.layer?.cornerRadius = 6.0
|
||||
}
|
||||
|
||||
panel.contentView?.addSubview(candidateView)
|
||||
|
||||
// MARK: Add Buttons
|
||||
|
||||
contentRect.size = NSSize(width: 20.0, height: 10.0) // Reduce the button width
|
||||
let buttonAttribute: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: 9.0)]
|
||||
|
||||
nextPageButton = .init(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(nextPageButton.bounds)
|
||||
nextPageButton.wantsLayer = true
|
||||
nextPageButton.layer?.masksToBounds = true
|
||||
nextPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
nextPageButton.layer?.borderWidth = 0.0
|
||||
nextPageButton.setButtonType(.momentaryLight)
|
||||
nextPageButton.bezelStyle = .disclosure
|
||||
nextPageButton.userInterfaceLayoutDirection = .leftToRight
|
||||
nextPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Next Page Arrow
|
||||
prevPageButton = .init(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(prevPageButton.bounds)
|
||||
prevPageButton.wantsLayer = true
|
||||
prevPageButton.layer?.masksToBounds = true
|
||||
prevPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
prevPageButton.layer?.borderWidth = 0.0
|
||||
prevPageButton.setButtonType(.momentaryLight)
|
||||
prevPageButton.bezelStyle = .disclosure
|
||||
prevPageButton.userInterfaceLayoutDirection = .rightToLeft
|
||||
prevPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Previous Page Arrow
|
||||
panel.contentView?.addSubview(nextPageButton)
|
||||
panel.contentView?.addSubview(prevPageButton)
|
||||
|
||||
// MARK: Add Page Counter
|
||||
|
||||
contentRect.size = NSSize(width: 40.0, height: 20.0)
|
||||
pageCounterLabel = .init(frame: contentRect)
|
||||
pageCounterLabel.isEditable = false
|
||||
pageCounterLabel.isSelectable = false
|
||||
pageCounterLabel.isBezeled = false
|
||||
pageCounterLabel.textColor = NSColor(
|
||||
red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00
|
||||
)
|
||||
pageCounterLabel.drawsBackground = true
|
||||
pageCounterLabel.backgroundColor = NSColor(
|
||||
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
|
||||
)
|
||||
panel.contentView?.addSubview(pageCounterLabel)
|
||||
|
||||
// MARK: Post-Init()
|
||||
|
||||
super.init(window: panel)
|
||||
currentLayout = layout
|
||||
|
||||
candidateView.target = self
|
||||
candidateView.action = #selector(candidateViewMouseDidClick(_:))
|
||||
|
||||
nextPageButton.target = self
|
||||
nextPageButton.action = #selector(pageButtonAction(_:))
|
||||
|
||||
prevPageButton.target = self
|
||||
prevPageButton.action = #selector(pageButtonAction(_:))
|
||||
|
||||
pageCounterLabel.font = pageCounterFont
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func reloadData() {
|
||||
candidateView.highlightedIndex = 0
|
||||
currentPageIndex = 0
|
||||
layoutCandidateView()
|
||||
}
|
||||
|
||||
override public func showNextPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightNextCandidate() }
|
||||
if currentPageIndex + 1 >= pageCount { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func showPreviousPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightPreviousCandidate() }
|
||||
if currentPageIndex == 0 { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightNextCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
|
||||
? 0 : selectedCandidateIndex + 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightPreviousCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex == 0)
|
||||
? delegate.candidateCountForController(self) - 1 : selectedCandidateIndex - 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func candidateIndexAtKeyLabelIndex(_ index: Int) -> Int {
|
||||
guard let delegate = delegate else {
|
||||
return Int.max
|
||||
}
|
||||
|
||||
let result = currentPageIndex * keyLabels.count + index
|
||||
return result < delegate.candidateCountForController(self) ? result : Int.max
|
||||
}
|
||||
|
||||
override public var selectedCandidateIndex: Int {
|
||||
get {
|
||||
currentPageIndex * keyLabels.count + candidateView.highlightedIndex
|
||||
}
|
||||
set {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
let keyLabelCount = keyLabels.count
|
||||
if newValue < delegate.candidateCountForController(self) {
|
||||
currentPageIndex = newValue / keyLabelCount
|
||||
candidateView.highlightedIndex = newValue % keyLabelCount
|
||||
layoutCandidateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ctlCandidateUniversal {
|
||||
private var pageCount: Int {
|
||||
guard let delegate = delegate else {
|
||||
return 0
|
||||
}
|
||||
let totalCount = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = keyLabels.count
|
||||
return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
// 用來顯示頁面計數器的 NSFont。因為結果可 nil,所以最好 guard-let 再用。
|
||||
private var pageCounterFont: NSFont? {
|
||||
var pointSize: CGFloat { candidateView.fractionFontSize }
|
||||
let systemFontDesc = NSFont.systemFont(ofSize: pointSize, weight: .light).fontDescriptor
|
||||
let fractionFontDesc = systemFontDesc.addingAttributes(
|
||||
[
|
||||
NSFontDescriptor.AttributeName.traits: [
|
||||
[
|
||||
NSFontDescriptor.FeatureKey.typeIdentifier: kFractionsType,
|
||||
NSFontDescriptor.FeatureKey.selectorIdentifier: kDiagonalFractionsSelector,
|
||||
]
|
||||
]
|
||||
]
|
||||
)
|
||||
return NSFont(descriptor: fractionFontDesc, size: pointSize) ?? nil
|
||||
}
|
||||
|
||||
// 用來生成拿給頁面計數器用的顯示字串。
|
||||
// TODO: 這衰洨的 pageCount 總是返回空字串,需要調查。
|
||||
private var pageCounterText: String {
|
||||
if pageCount < 2 { return .init() }
|
||||
return "\(currentPageIndex + 1)/"
|
||||
}
|
||||
|
||||
private func layoutCandidateView() {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
|
||||
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
|
||||
var candidates = [String]()
|
||||
let count = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = keyLabels.count
|
||||
|
||||
let begin = currentPageIndex * keyLabelCount
|
||||
for index in begin..<min(begin + keyLabelCount, count) {
|
||||
let candidate = delegate.ctlCandidate(self, candidateAtIndex: index)
|
||||
candidates.append(candidate)
|
||||
}
|
||||
candidateView.set(
|
||||
keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates
|
||||
)
|
||||
var newSize = candidateView.sizeForView
|
||||
var frameRect = candidateView.frame
|
||||
frameRect.size = newSize
|
||||
candidateView.frame = frameRect
|
||||
let counterHeight: CGFloat = newSize.height - 24
|
||||
|
||||
if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow {
|
||||
var buttonRect = nextPageButton.frame
|
||||
let spacing: CGFloat = 0.0
|
||||
|
||||
if currentLayout == .horizontal { buttonRect.size.height = floor(newSize.height / 2) }
|
||||
let buttonOriginY: CGFloat = {
|
||||
if currentLayout == .vertical {
|
||||
return counterHeight
|
||||
}
|
||||
return (newSize.height - (buttonRect.size.height * 2.0 + spacing)) / 2.0
|
||||
}()
|
||||
buttonRect.origin = NSPoint(x: newSize.width, y: buttonOriginY)
|
||||
nextPageButton.frame = buttonRect
|
||||
buttonRect.origin = NSPoint(
|
||||
x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing
|
||||
)
|
||||
prevPageButton.frame = buttonRect
|
||||
newSize.width += 20
|
||||
nextPageButton.isHidden = false
|
||||
prevPageButton.isHidden = false
|
||||
} else {
|
||||
nextPageButton.isHidden = true
|
||||
prevPageButton.isHidden = true
|
||||
}
|
||||
|
||||
if !pageCounterText.isEmpty {
|
||||
let attrString = NSAttributedString(
|
||||
string: pageCounterText.appending(String(pageCount)),
|
||||
attributes: [
|
||||
.font: pageCounterFont as AnyObject
|
||||
]
|
||||
)
|
||||
pageCounterLabel.attributedStringValue = attrString
|
||||
var rect = attrString.boundingRect(
|
||||
with: NSSize(width: 1600.0, height: 1600.0),
|
||||
options: .usesLineFragmentOrigin
|
||||
)
|
||||
|
||||
rect.size.height += 3
|
||||
let rectOriginY: CGFloat =
|
||||
(currentLayout == .horizontal)
|
||||
? (newSize.height - rect.height) / 2
|
||||
: counterHeight
|
||||
let rectOriginX: CGFloat =
|
||||
mgrPrefs.showPageButtonsInCandidateWindow
|
||||
? newSize.width
|
||||
: newSize.width + 4
|
||||
rect.origin = NSPoint(x: rectOriginX, y: rectOriginY)
|
||||
pageCounterLabel.frame = rect
|
||||
newSize.width += rect.width + 4
|
||||
pageCounterLabel.isHidden = false
|
||||
} else {
|
||||
pageCounterLabel.isHidden = true
|
||||
}
|
||||
|
||||
frameRect = window?.frame ?? NSRect.zero
|
||||
|
||||
let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)
|
||||
frameRect.size = newSize
|
||||
frameRect.origin = NSPoint(x: topLeftPoint.x, y: topLeftPoint.y - frameRect.size.height)
|
||||
window?.setFrame(frameRect, display: false)
|
||||
candidateView.setNeedsDisplay(candidateView.bounds)
|
||||
}
|
||||
|
||||
@objc private func pageButtonAction(_ sender: Any) {
|
||||
guard let sender = sender as? NSButton else {
|
||||
return
|
||||
}
|
||||
if sender == nextPageButton {
|
||||
_ = showNextPage()
|
||||
} else if sender == prevPageButton {
|
||||
_ = showPreviousPage()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func candidateViewMouseDidClick(_: Any) {
|
||||
delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex)
|
||||
}
|
||||
}
|
|
@ -1,478 +0,0 @@
|
|||
// Copyright (c) 2011 and onwards The OpenVanilla Project (MIT License).
|
||||
// All possible vChewing-specific modifications are of:
|
||||
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
1. The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
2. No trademark license is granted to use the trade names, trademarks, service
|
||||
marks, or product names of Contributor, except as required to fulfill notice
|
||||
requirements above.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import Cocoa
|
||||
|
||||
private class VerticalCandidateView: NSView {
|
||||
var highlightedIndex: UInt = 0
|
||||
var action: Selector?
|
||||
weak var target: AnyObject?
|
||||
|
||||
private var keyLabels: [String] = []
|
||||
private var displayedCandidates: [String] = []
|
||||
private var dispCandidatesWithLabels: [String] = []
|
||||
private var keyLabelHeight: CGFloat = 0
|
||||
private var keyLabelWidth: CGFloat = 0
|
||||
private var candidateTextHeight: CGFloat = 0
|
||||
private var cellPadding: CGFloat = 0
|
||||
private var keyLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var candidateWithLabelAttrDict: [NSAttributedString.Key: AnyObject] = [:]
|
||||
private var windowWidth: CGFloat = 0
|
||||
private var elementWidths: [CGFloat] = []
|
||||
private var elementHeights: [CGFloat] = []
|
||||
private var trackingHighlightedIndex: UInt = .max
|
||||
|
||||
override var isFlipped: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var sizeForView: NSSize {
|
||||
var result = NSSize.zero
|
||||
|
||||
if !elementWidths.isEmpty {
|
||||
result.width = windowWidth
|
||||
result.height = elementHeights.reduce(0, +)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@objc(setKeyLabels:displayedCandidates:)
|
||||
func set(keyLabels labels: [String], displayedCandidates candidates: [String]) {
|
||||
let count = min(labels.count, candidates.count)
|
||||
keyLabels = Array(labels[0..<count])
|
||||
displayedCandidates = Array(candidates[0..<count])
|
||||
dispCandidatesWithLabels = zip(keyLabels, displayedCandidates).map { $0 + $1 }
|
||||
|
||||
var newWidths = [CGFloat]()
|
||||
var calculatedWindowWidth = CGFloat()
|
||||
var newHeights = [CGFloat]()
|
||||
let baseSize = NSSize(width: 10240.0, height: 10240.0)
|
||||
for index in 0..<count {
|
||||
let rctCandidate = (dispCandidatesWithLabels[index] as NSString).boundingRect(
|
||||
with: baseSize, options: .usesLineFragmentOrigin,
|
||||
attributes: candidateWithLabelAttrDict
|
||||
)
|
||||
let cellWidth = rctCandidate.size.width + cellPadding
|
||||
let cellHeight = rctCandidate.size.height + cellPadding
|
||||
if calculatedWindowWidth < rctCandidate.size.width {
|
||||
calculatedWindowWidth = rctCandidate.size.width + cellPadding
|
||||
}
|
||||
newWidths.append(cellWidth)
|
||||
newHeights.append(cellHeight)
|
||||
}
|
||||
elementWidths = newWidths
|
||||
elementHeights = newHeights
|
||||
windowWidth = round(calculatedWindowWidth + cellPadding) // 防止邊框粗細不一
|
||||
}
|
||||
|
||||
@objc(setKeyLabelFont:candidateFont:)
|
||||
func set(keyLabelFont labelFont: NSFont, candidateFont: NSFont) {
|
||||
let paraStyle = NSMutableParagraphStyle()
|
||||
paraStyle.setParagraphStyle(NSParagraphStyle.default)
|
||||
paraStyle.alignment = .left
|
||||
|
||||
candidateWithLabelAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // We still need this dummy section to make sure that…
|
||||
// …the space occupations of the candidates are correct.
|
||||
|
||||
keyLabelAttrDict = [
|
||||
.font: labelFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.verticalGlyphForm: true as AnyObject,
|
||||
.foregroundColor: NSColor.secondaryLabelColor,
|
||||
] // Candidate phrase text color
|
||||
candidateAttrDict = [
|
||||
.font: candidateFont,
|
||||
.paragraphStyle: paraStyle,
|
||||
.foregroundColor: NSColor.labelColor,
|
||||
] // Candidate index text color
|
||||
let labelFontSize = labelFont.pointSize
|
||||
let candidateFontSize = candidateFont.pointSize
|
||||
let biggestSize = max(labelFontSize, candidateFontSize)
|
||||
keyLabelWidth = ceil(labelFontSize)
|
||||
keyLabelHeight = ceil(labelFontSize * 2)
|
||||
candidateTextHeight = ceil(candidateFontSize * 1.20)
|
||||
cellPadding = ceil(biggestSize / 4.0) * 2
|
||||
}
|
||||
|
||||
override func draw(_: NSRect) {
|
||||
let bounds = bounds
|
||||
NSColor.controlBackgroundColor.setFill() // Candidate list panel base background
|
||||
NSBezierPath.fill(bounds)
|
||||
|
||||
NSColor.systemGray.withAlphaComponent(0.75).setStroke()
|
||||
|
||||
NSBezierPath.strokeLine(
|
||||
from: NSPoint(x: bounds.size.width, y: 0.0),
|
||||
to: NSPoint(x: bounds.size.width, y: bounds.size.height)
|
||||
)
|
||||
|
||||
var accuHeight: CGFloat = 0
|
||||
for (index, elementHeight) in elementHeights.enumerated() {
|
||||
let currentHeight = elementHeight
|
||||
let rctCandidateArea = NSRect(
|
||||
x: 0.0, y: accuHeight, width: windowWidth, height: candidateTextHeight + cellPadding
|
||||
)
|
||||
let rctLabel = NSRect(
|
||||
x: cellPadding / 2 - 1, y: accuHeight + cellPadding / 2, width: keyLabelWidth,
|
||||
height: keyLabelHeight * 2.0
|
||||
)
|
||||
let rctCandidatePhrase = NSRect(
|
||||
x: cellPadding / 2 - 1 + keyLabelWidth, y: accuHeight + cellPadding / 2 - 1,
|
||||
width: windowWidth - keyLabelWidth, height: candidateTextHeight
|
||||
)
|
||||
|
||||
var activeCandidateIndexAttr = keyLabelAttrDict
|
||||
var activeCandidateAttr = candidateAttrDict
|
||||
if index == highlightedIndex {
|
||||
let colorBlendAmount: CGFloat = IME.isDarkMode() ? 0.25 : 0
|
||||
// The background color of the highlightened candidate
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
NSColor.systemRed.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
case InputMode.imeModeCHT:
|
||||
NSColor.systemBlue.blended(
|
||||
withFraction: colorBlendAmount,
|
||||
of: NSColor.controlBackgroundColor
|
||||
)!
|
||||
.setFill()
|
||||
default:
|
||||
NSColor.alternateSelectedControlColor.setFill()
|
||||
}
|
||||
// Highlightened index text color
|
||||
activeCandidateIndexAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
.withAlphaComponent(0.84)
|
||||
// Highlightened phrase text color
|
||||
activeCandidateAttr[.foregroundColor] = NSColor.selectedMenuItemTextColor
|
||||
} else {
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
}
|
||||
switch ctlInputMethod.currentKeyHandler.inputMode {
|
||||
case InputMode.imeModeCHS:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hans" as AnyObject
|
||||
}
|
||||
case InputMode.imeModeCHT:
|
||||
if #available(macOS 12.0, *) {
|
||||
activeCandidateAttr[.languageIdentifier] = "zh-Hant" as AnyObject
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
NSBezierPath.fill(rctCandidateArea)
|
||||
(keyLabels[index] as NSString).draw(
|
||||
in: rctLabel, withAttributes: activeCandidateIndexAttr
|
||||
)
|
||||
(displayedCandidates[index] as NSString).draw(
|
||||
in: rctCandidatePhrase, withAttributes: activeCandidateAttr
|
||||
)
|
||||
accuHeight += currentHeight
|
||||
}
|
||||
}
|
||||
|
||||
private func findHitIndex(event: NSEvent) -> UInt? {
|
||||
let location = convert(event.locationInWindow, to: nil)
|
||||
if !bounds.contains(location) {
|
||||
return nil
|
||||
}
|
||||
var accuHeight: CGFloat = 0.0
|
||||
for (index, elementHeight) in elementHeights.enumerated() {
|
||||
let currentHeight = elementHeight
|
||||
|
||||
if location.y >= accuHeight, location.y <= accuHeight + currentHeight {
|
||||
return UInt(index)
|
||||
}
|
||||
accuHeight += currentHeight
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
override func mouseUp(with event: NSEvent) {
|
||||
trackingHighlightedIndex = highlightedIndex
|
||||
guard let newIndex = findHitIndex(event: event) else {
|
||||
return
|
||||
}
|
||||
highlightedIndex = newIndex
|
||||
setNeedsDisplay(bounds)
|
||||
}
|
||||
|
||||
override func mouseDown(with event: NSEvent) {
|
||||
guard let newIndex = findHitIndex(event: event) else {
|
||||
return
|
||||
}
|
||||
var triggerAction = false
|
||||
if newIndex == highlightedIndex {
|
||||
triggerAction = true
|
||||
} else {
|
||||
highlightedIndex = trackingHighlightedIndex
|
||||
}
|
||||
|
||||
trackingHighlightedIndex = 0
|
||||
setNeedsDisplay(bounds)
|
||||
if triggerAction {
|
||||
if let target = target as? NSObject, let action = action {
|
||||
target.perform(action, with: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ctlCandidateVertical: ctlCandidate {
|
||||
private var candidateView: VerticalCandidateView
|
||||
private var prevPageButton: NSButton
|
||||
private var nextPageButton: NSButton
|
||||
private var currentPageIndex: UInt = 0
|
||||
|
||||
public init() {
|
||||
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
|
||||
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
|
||||
let panel = NSPanel(
|
||||
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
|
||||
)
|
||||
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1)
|
||||
panel.hasShadow = true
|
||||
panel.isOpaque = false
|
||||
panel.backgroundColor = NSColor.clear
|
||||
|
||||
contentRect.origin = NSPoint.zero
|
||||
candidateView = VerticalCandidateView(frame: contentRect)
|
||||
|
||||
candidateView.wantsLayer = true
|
||||
candidateView.layer?.borderColor =
|
||||
NSColor.selectedMenuItemTextColor.withAlphaComponent(0.10).cgColor
|
||||
candidateView.layer?.borderWidth = 1.0
|
||||
if #available(macOS 10.13, *) {
|
||||
candidateView.layer?.cornerRadius = 6.0
|
||||
}
|
||||
|
||||
panel.contentView?.addSubview(candidateView)
|
||||
|
||||
contentRect.size = NSSize(width: 20.0, height: 10.0) // Reduce the button width
|
||||
let buttonAttribute: [NSAttributedString.Key: Any] = [.font: NSFont.systemFont(ofSize: 9.0)]
|
||||
|
||||
nextPageButton = NSButton(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(nextPageButton.bounds)
|
||||
nextPageButton.wantsLayer = true
|
||||
nextPageButton.layer?.masksToBounds = true
|
||||
nextPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
nextPageButton.layer?.borderWidth = 0.0
|
||||
nextPageButton.setButtonType(.momentaryLight)
|
||||
nextPageButton.bezelStyle = .disclosure
|
||||
nextPageButton.userInterfaceLayoutDirection = .leftToRight
|
||||
nextPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Next Page Arrow
|
||||
prevPageButton = NSButton(frame: contentRect)
|
||||
NSColor.controlBackgroundColor.setFill()
|
||||
NSBezierPath.fill(prevPageButton.bounds)
|
||||
prevPageButton.wantsLayer = true
|
||||
prevPageButton.layer?.masksToBounds = true
|
||||
prevPageButton.layer?.borderColor = NSColor.clear.cgColor
|
||||
prevPageButton.layer?.borderWidth = 0.0
|
||||
prevPageButton.setButtonType(.momentaryLight)
|
||||
prevPageButton.bezelStyle = .disclosure
|
||||
prevPageButton.userInterfaceLayoutDirection = .rightToLeft
|
||||
prevPageButton.attributedTitle = NSMutableAttributedString(
|
||||
string: " ", attributes: buttonAttribute
|
||||
) // Previous Page Arrow
|
||||
panel.contentView?.addSubview(nextPageButton)
|
||||
panel.contentView?.addSubview(prevPageButton)
|
||||
|
||||
super.init(window: panel)
|
||||
|
||||
candidateView.target = self
|
||||
candidateView.action = #selector(candidateViewMouseDidClick(_:))
|
||||
|
||||
nextPageButton.target = self
|
||||
nextPageButton.action = #selector(pageButtonAction(_:))
|
||||
|
||||
prevPageButton.target = self
|
||||
prevPageButton.action = #selector(pageButtonAction(_:))
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder _: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override public func reloadData() {
|
||||
candidateView.highlightedIndex = 0
|
||||
currentPageIndex = 0
|
||||
layoutCandidateView()
|
||||
}
|
||||
|
||||
override public func showNextPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightNextCandidate() }
|
||||
if currentPageIndex + 1 >= pageCount { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex + 1 >= pageCount) ? 0 : currentPageIndex + 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func showPreviousPage() -> Bool {
|
||||
guard delegate != nil else { return false }
|
||||
if pageCount == 1 { return highlightPreviousCandidate() }
|
||||
if currentPageIndex == 0 { clsSFX.beep() }
|
||||
currentPageIndex = (currentPageIndex == 0) ? pageCount - 1 : currentPageIndex - 1
|
||||
candidateView.highlightedIndex = 0
|
||||
layoutCandidateView()
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightNextCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex + 1 >= delegate.candidateCountForController(self))
|
||||
? 0 : selectedCandidateIndex + 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func highlightPreviousCandidate() -> Bool {
|
||||
guard let delegate = delegate else { return false }
|
||||
selectedCandidateIndex =
|
||||
(selectedCandidateIndex == 0)
|
||||
? delegate.candidateCountForController(self) - 1 : selectedCandidateIndex - 1
|
||||
return true
|
||||
}
|
||||
|
||||
override public func candidateIndexAtKeyLabelIndex(_ index: UInt) -> UInt {
|
||||
guard let delegate = delegate else {
|
||||
return UInt.max
|
||||
}
|
||||
|
||||
let result = currentPageIndex * UInt(keyLabels.count) + index
|
||||
return result < delegate.candidateCountForController(self) ? result : UInt.max
|
||||
}
|
||||
|
||||
override public var selectedCandidateIndex: UInt {
|
||||
get {
|
||||
currentPageIndex * UInt(keyLabels.count) + candidateView.highlightedIndex
|
||||
}
|
||||
set {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
if newValue < delegate.candidateCountForController(self) {
|
||||
currentPageIndex = newValue / keyLabelCount
|
||||
candidateView.highlightedIndex = newValue % keyLabelCount
|
||||
layoutCandidateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ctlCandidateVertical {
|
||||
private var pageCount: UInt {
|
||||
guard let delegate = delegate else {
|
||||
return 0
|
||||
}
|
||||
let totalCount = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
return totalCount / keyLabelCount + ((totalCount % keyLabelCount) != 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
private func layoutCandidateView() {
|
||||
guard let delegate = delegate else {
|
||||
return
|
||||
}
|
||||
|
||||
candidateView.set(keyLabelFont: keyLabelFont, candidateFont: candidateFont)
|
||||
var candidates = [String]()
|
||||
let count = delegate.candidateCountForController(self)
|
||||
let keyLabelCount = UInt(keyLabels.count)
|
||||
|
||||
let begin = currentPageIndex * keyLabelCount
|
||||
for index in begin..<min(begin + keyLabelCount, count) {
|
||||
let candidate = delegate.ctlCandidate(self, candidateAtIndex: index)
|
||||
candidates.append(candidate)
|
||||
}
|
||||
candidateView.set(
|
||||
keyLabels: keyLabels.map(\.displayedText), displayedCandidates: candidates
|
||||
)
|
||||
var newSize = candidateView.sizeForView
|
||||
var frameRect = candidateView.frame
|
||||
frameRect.size = newSize
|
||||
candidateView.frame = frameRect
|
||||
|
||||
if pageCount > 1, mgrPrefs.showPageButtonsInCandidateWindow {
|
||||
var buttonRect = nextPageButton.frame
|
||||
let spacing: CGFloat = 0.0
|
||||
|
||||
// buttonRect.size.height = floor(candidateTextHeight + cellPadding / 2)
|
||||
|
||||
let buttonOriginY = (newSize.height - (buttonRect.size.height * 2.0 + spacing)) // / 2.0
|
||||
buttonRect.origin = NSPoint(x: newSize.width, y: buttonOriginY)
|
||||
nextPageButton.frame = buttonRect
|
||||
|
||||
buttonRect.origin = NSPoint(
|
||||
x: newSize.width, y: buttonOriginY + buttonRect.size.height + spacing
|
||||
)
|
||||
prevPageButton.frame = buttonRect
|
||||
|
||||
newSize.width += 20
|
||||
nextPageButton.isHidden = false
|
||||
prevPageButton.isHidden = false
|
||||
} else {
|
||||
nextPageButton.isHidden = true
|
||||
prevPageButton.isHidden = true
|
||||
}
|
||||
|
||||
frameRect = window?.frame ?? NSRect.zero
|
||||
|
||||
let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height)
|
||||
frameRect.size = newSize
|
||||
frameRect.origin = NSPoint(x: topLeftPoint.x, y: topLeftPoint.y - frameRect.size.height)
|
||||
window?.setFrame(frameRect, display: false)
|
||||
candidateView.setNeedsDisplay(candidateView.bounds)
|
||||
}
|
||||
|
||||
@objc private func pageButtonAction(_ sender: Any) {
|
||||
guard let sender = sender as? NSButton else {
|
||||
return
|
||||
}
|
||||
if sender == nextPageButton {
|
||||
_ = showNextPage()
|
||||
} else if sender == prevPageButton {
|
||||
_ = showPreviousPage()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func candidateViewMouseDidClick(_: Any) {
|
||||
delegate?.ctlCandidate(self, didSelectCandidateAtIndex: selectedCandidateIndex)
|
||||
}
|
||||
}
|
|
@ -35,6 +35,8 @@ struct suiPrefPaneDictionary: View {
|
|||
@State private var selEnableCNS11643: Bool = UserDefaults.standard.bool(forKey: UserDef.kCNS11643Enabled)
|
||||
@State private var selEnableSymbolInputSupport: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kSymbolInputEnabled)
|
||||
@State private var selAllowBoostingSingleKanjiAsUserPhrase: Bool = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kAllowBoostingSingleKanjiAsUserPhrase)
|
||||
private let contentWidth: Double = {
|
||||
switch mgrPrefs.appleLanguages[0] {
|
||||
case "ja":
|
||||
|
@ -65,7 +67,7 @@ struct suiPrefPaneDictionary: View {
|
|||
IME.dlgOpenPath.canChooseDirectories = true
|
||||
|
||||
let bolPreviousFolderValidity = mgrLangModel.checkIfSpecifiedUserDataFolderValid(
|
||||
NSString(string: mgrPrefs.userDataFolderSpecified).expandingTildeInPath)
|
||||
mgrPrefs.userDataFolderSpecified.expandingTildeInPath)
|
||||
|
||||
if let window = ctlPrefUI.shared.controller.window {
|
||||
IME.dlgOpenPath.beginSheetModal(for: window) { result in
|
||||
|
@ -113,11 +115,14 @@ struct suiPrefPaneDictionary: View {
|
|||
mgrPrefs.shouldAutoReloadUserDataFiles = value
|
||||
}
|
||||
Divider()
|
||||
Toggle(LocalizedStringKey("Enable CNS11643 Support (2022-04-27)"), isOn: $selEnableCNS11643)
|
||||
.onChange(of: selEnableCNS11643) { value in
|
||||
mgrPrefs.cns11643Enabled = value
|
||||
mgrLangModel.setCNSEnabled(value)
|
||||
}
|
||||
Toggle(
|
||||
LocalizedStringKey("Enable CNS11643 Support (2022-04-27)"),
|
||||
isOn: $selEnableCNS11643
|
||||
)
|
||||
.onChange(of: selEnableCNS11643) { value in
|
||||
mgrPrefs.cns11643Enabled = value
|
||||
mgrLangModel.setCNSEnabled(value)
|
||||
}
|
||||
Toggle(
|
||||
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
|
||||
isOn: $selEnableSymbolInputSupport
|
||||
|
@ -126,6 +131,13 @@ struct suiPrefPaneDictionary: View {
|
|||
mgrPrefs.symbolInputEnabled = value
|
||||
mgrLangModel.setSymbolEnabled(value)
|
||||
}
|
||||
Toggle(
|
||||
LocalizedStringKey("Allow boosting / excluding a candidate of single kanji"),
|
||||
isOn: $selAllowBoostingSingleKanjiAsUserPhrase
|
||||
)
|
||||
.onChange(of: selAllowBoostingSingleKanjiAsUserPhrase) { value in
|
||||
mgrPrefs.allowBoostingSingleKanjiAsUserPhrase = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,13 @@ struct suiPrefPaneGeneral: View {
|
|||
.preferenceDescription()
|
||||
Toggle(
|
||||
LocalizedStringKey("Show page buttons in candidate window"), isOn: $selShowPageButtonsInCandidateUI
|
||||
).controlSize(.small)
|
||||
).onChange(
|
||||
of: selShowPageButtonsInCandidateUI,
|
||||
perform: { value in
|
||||
mgrPrefs.showPageButtonsInCandidateWindow = value
|
||||
}
|
||||
)
|
||||
.controlSize(.small)
|
||||
}
|
||||
Preferences.Section(bottomDivider: true, label: { Text(LocalizedStringKey("Output Settings:")) }) {
|
||||
Toggle(
|
||||
|
|
|
@ -27,8 +27,17 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
import Cocoa
|
||||
|
||||
public class TooltipController: NSWindowController {
|
||||
static var backgroundColor = NSColor.windowBackgroundColor
|
||||
static var textColor = NSColor.windowBackgroundColor
|
||||
public enum ColorStates {
|
||||
case normal
|
||||
case redAlert
|
||||
case warning
|
||||
case denialOverflow
|
||||
case denialInsufficiency
|
||||
case prompt
|
||||
}
|
||||
|
||||
private var backgroundColor = NSColor.windowBackgroundColor
|
||||
private var textColor = NSColor.windowBackgroundColor
|
||||
private var messageTextField: NSTextField
|
||||
private var tooltip: String = "" {
|
||||
didSet {
|
||||
|
@ -50,12 +59,11 @@ public class TooltipController: NSWindowController {
|
|||
messageTextField.isEditable = false
|
||||
messageTextField.isSelectable = false
|
||||
messageTextField.isBezeled = false
|
||||
messageTextField.textColor = TooltipController.textColor
|
||||
messageTextField.textColor = textColor
|
||||
messageTextField.drawsBackground = true
|
||||
messageTextField.backgroundColor = TooltipController.backgroundColor
|
||||
messageTextField.backgroundColor = backgroundColor
|
||||
messageTextField.font = .systemFont(ofSize: NSFont.systemFontSize(for: .small))
|
||||
panel.contentView?.addSubview(messageTextField)
|
||||
|
||||
super.init(window: panel)
|
||||
}
|
||||
|
||||
|
@ -65,13 +73,56 @@ public class TooltipController: NSWindowController {
|
|||
}
|
||||
|
||||
public func show(tooltip: String, at point: NSPoint) {
|
||||
messageTextField.textColor = TooltipController.textColor
|
||||
messageTextField.backgroundColor = TooltipController.backgroundColor
|
||||
messageTextField.textColor = textColor
|
||||
messageTextField.backgroundColor = backgroundColor
|
||||
self.tooltip = tooltip
|
||||
window?.orderFront(nil)
|
||||
set(windowLocation: point)
|
||||
}
|
||||
|
||||
public func setColor(state: ColorStates) {
|
||||
switch state {
|
||||
case .normal:
|
||||
backgroundColor = NSColor(
|
||||
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
|
||||
)
|
||||
textColor = NSColor.white
|
||||
case .redAlert:
|
||||
backgroundColor = NSColor(
|
||||
red: 0.55, green: 0.00, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
textColor = NSColor.white
|
||||
case .warning:
|
||||
backgroundColor = NSColor.purple
|
||||
textColor = NSColor.white
|
||||
case .denialOverflow:
|
||||
backgroundColor = NSColor(
|
||||
red: 0.13, green: 0.08, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
textColor = NSColor(
|
||||
red: 1.00, green: 0.60, blue: 0.00, alpha: 1.00
|
||||
)
|
||||
case .denialInsufficiency:
|
||||
backgroundColor = NSColor(
|
||||
red: 0.18, green: 0.18, blue: 0.18, alpha: 1.00
|
||||
)
|
||||
textColor = NSColor(
|
||||
red: 0.86, green: 0.86, blue: 0.86, alpha: 1.00
|
||||
)
|
||||
case .prompt:
|
||||
backgroundColor = NSColor(
|
||||
red: 0.00, green: 0.18, blue: 0.13, alpha: 1.00
|
||||
)
|
||||
textColor = NSColor(
|
||||
red: 0.00, green: 1.00, blue: 0.74, alpha: 1.00
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func resetColor() {
|
||||
setColor(state: .normal)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func hide() {
|
||||
window?.orderOut(nil)
|
||||
|
|
|
@ -44,7 +44,8 @@ class ctlNonModalAlertWindow: NSWindowController {
|
|||
title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?,
|
||||
cancelAsDefault: Bool, delegate: ctlNonModalAlertWindowDelegate?
|
||||
) {
|
||||
if window?.isVisible == true {
|
||||
guard let window = window else { return }
|
||||
if window.isVisible == true {
|
||||
self.delegate?.ctlNonModalAlertWindowDidCancel(self)
|
||||
}
|
||||
|
||||
|
@ -76,13 +77,13 @@ class ctlNonModalAlertWindow: NSWindowController {
|
|||
|
||||
if cancelButtonTitle != nil {
|
||||
if cancelAsDefault {
|
||||
window?.defaultButtonCell = cancelButton.cell as? NSButtonCell
|
||||
window.defaultButtonCell = cancelButton.cell as? NSButtonCell
|
||||
} else {
|
||||
cancelButton.keyEquivalent = " "
|
||||
window?.defaultButtonCell = confirmButton.cell as? NSButtonCell
|
||||
window.defaultButtonCell = confirmButton.cell as? NSButtonCell
|
||||
}
|
||||
} else {
|
||||
window?.defaultButtonCell = confirmButton.cell as? NSButtonCell
|
||||
window.defaultButtonCell = confirmButton.cell as? NSButtonCell
|
||||
}
|
||||
|
||||
titleTextField.stringValue = title
|
||||
|
@ -103,12 +104,12 @@ class ctlNonModalAlertWindow: NSWindowController {
|
|||
newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height)
|
||||
contentTextField.frame = newFrame
|
||||
|
||||
var windowFrame = window?.frame ?? NSRect.zero
|
||||
var windowFrame = window.frame
|
||||
windowFrame.size.height += (newFrame.size.height - oldFrame.size.height)
|
||||
window?.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1)
|
||||
window?.setFrame(windowFrame, display: true)
|
||||
window?.center()
|
||||
window?.makeKeyAndOrderFront(self)
|
||||
window.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1)
|
||||
window.setFrame(windowFrame, display: true)
|
||||
window.center()
|
||||
window.makeKeyAndOrderFront(self)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ class ctlPrefWindow: NSWindowController {
|
|||
IME.dlgOpenPath.canChooseDirectories = true
|
||||
|
||||
let bolPreviousFolderValidity = mgrLangModel.checkIfSpecifiedUserDataFolderValid(
|
||||
NSString(string: mgrPrefs.userDataFolderSpecified).expandingTildeInPath)
|
||||
mgrPrefs.userDataFolderSpecified.expandingTildeInPath)
|
||||
|
||||
if window != nil {
|
||||
IME.dlgOpenPath.beginSheetModal(for: window!) { result in
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -680,9 +681,9 @@
|
|||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="p7V-IN-OTr">
|
||||
<rect key="frame" x="19" y="295.5" width="406" height="17"/>
|
||||
<rect key="frame" x="19" y="296.5" width="406" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="KGx-QR-wgI"/>
|
||||
<constraint firstAttribute="height" constant="16" id="Ieq-hl-01l"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Automatically reload user data files if changes detected" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="f8i-69-zxm">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
|
@ -693,9 +694,9 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="O4B-Z2-XYi">
|
||||
<rect key="frame" x="19" y="273.5" width="406" height="17"/>
|
||||
<rect key="frame" x="19" y="274.5" width="406" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="60g-xI-n1n"/>
|
||||
<constraint firstAttribute="height" constant="16" id="2gi-B5-vjz"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Enable symbol input support (incl. certain emoji symbols)" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="hSv-LJ-Cq3" userLabel="chkSymbolEnabled">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
|
@ -735,9 +736,9 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Yaj-QY-7xV" userLabel="chkCNSSupport">
|
||||
<rect key="frame" x="19" y="251.5" width="406" height="17"/>
|
||||
<rect key="frame" x="19" y="252.5" width="406" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="XP3-SR-9Ha"/>
|
||||
<constraint firstAttribute="height" constant="16" id="vin-Cj-dDq"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Enable CNS11643 Support (2022-04-27)" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="W24-T4-cg0">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
|
@ -748,28 +749,44 @@
|
|||
<binding destination="32" name="value" keyPath="values.CNS11643Enabled" id="Pbx-Gt-upm"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Hyc-Nw-dET">
|
||||
<rect key="frame" x="19" y="230.5" width="406" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="drz-HW-cX1"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Allow boosting / excluding a candidate of single kanji" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="chkAllowBoostingSingleKanjiAsUserPhrase" userLabel="chkAllowBoostingSingleKanjiAsUserPhrase">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="32" name="value" keyPath="values.AllowBoostingSingleKanjiAsUserPhrase" id="jiw-1V-C7x"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="s7t-Kk-EPu" firstAttribute="bottom" secondItem="MPN-np-SbT" secondAttribute="bottom" id="0Fo-ya-hQ9"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="trailing" secondItem="Yaj-QY-7xV" secondAttribute="trailing" id="2VU-kM-5xJ"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="O4B-Z2-XYi" secondAttribute="leading" id="6GG-yw-MYi"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="top" secondItem="p7V-IN-OTr" secondAttribute="bottom" constant="6" symbolic="YES" id="6H0-cT-DDa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="FUV-qx-xkC" secondAttribute="trailing" constant="20" symbolic="YES" id="6QR-tj-5cH"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="top" secondItem="p7V-IN-OTr" secondAttribute="bottom" constant="6" id="6tf-H0-CKc"/>
|
||||
<constraint firstItem="Yaj-QY-7xV" firstAttribute="trailing" secondItem="Hyc-Nw-dET" secondAttribute="trailing" id="6ty-e2-2yl"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="s7t-Kk-EPu" secondAttribute="leading" id="7KG-BW-e6E"/>
|
||||
<constraint firstItem="s7t-Kk-EPu" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="trailing" constant="-376" id="9at-E8-Bt1"/>
|
||||
<constraint firstItem="FUV-qx-xkC" firstAttribute="top" secondItem="cy4-aV-hhk" secondAttribute="top" constant="15" id="BZE-dD-V2R"/>
|
||||
<constraint firstItem="MPN-np-SbT" firstAttribute="top" secondItem="FUV-qx-xkC" secondAttribute="bottom" constant="14" id="Bp9-2u-f9E"/>
|
||||
<constraint firstItem="Yaj-QY-7xV" firstAttribute="leading" secondItem="Hyc-Nw-dET" secondAttribute="leading" id="DJV-E7-Wgv"/>
|
||||
<constraint firstItem="Yaj-QY-7xV" firstAttribute="top" secondItem="O4B-Z2-XYi" secondAttribute="bottom" constant="6" symbolic="YES" id="F6l-lB-OT5"/>
|
||||
<constraint firstItem="FUV-qx-xkC" firstAttribute="leading" secondItem="cy4-aV-hhk" secondAttribute="leading" constant="20" symbolic="YES" id="Hy2-ZC-cvb"/>
|
||||
<constraint firstItem="Hyc-Nw-dET" firstAttribute="top" secondItem="Yaj-QY-7xV" secondAttribute="bottom" constant="6" symbolic="YES" id="OCz-SW-av6"/>
|
||||
<constraint firstItem="s7t-Kk-EPu" firstAttribute="firstBaseline" secondItem="MPN-np-SbT" secondAttribute="baseline" id="OYH-gA-WcA"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="O4B-Z2-XYi" secondAttribute="trailing" id="Phu-Qi-Xup"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="top" secondItem="s7t-Kk-EPu" secondAttribute="bottom" constant="15" id="PyP-N9-MWb"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="s7t-Kk-EPu" secondAttribute="leading" id="QMQ-hR-c5W"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="jXe-xz-9Sd" secondAttribute="trailing" id="QQv-cA-dO9"/>
|
||||
<constraint firstItem="FUV-qx-xkC" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="leading" constant="-346" id="Zet-wH-kmC"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="leading" secondItem="Yaj-QY-7xV" secondAttribute="leading" id="aI8-ll-ybr"/>
|
||||
<constraint firstItem="jXe-xz-9Sd" firstAttribute="leading" secondItem="MPN-np-SbT" secondAttribute="trailing" constant="-1" id="cYQ-Rx-tuG"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="leading" secondItem="O4B-Z2-XYi" secondAttribute="leading" id="dm5-TK-PRw"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="leading" secondItem="Yaj-QY-7xV" secondAttribute="leading" id="qHY-fz-P9H"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="jXe-xz-9Sd" secondAttribute="trailing" id="uuG-Bg-2Vi"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="top" secondItem="s7t-Kk-EPu" secondAttribute="bottom" constant="14.5" id="hJ9-p8-clP"/>
|
||||
<constraint firstItem="O4B-Z2-XYi" firstAttribute="trailing" secondItem="Yaj-QY-7xV" secondAttribute="trailing" id="pZC-Vr-DTa"/>
|
||||
<constraint firstItem="p7V-IN-OTr" firstAttribute="trailing" secondItem="O4B-Z2-XYi" secondAttribute="trailing" id="s0C-8F-wWy"/>
|
||||
<constraint firstItem="s7t-Kk-EPu" firstAttribute="trailing" secondItem="FUV-qx-xkC" secondAttribute="trailing" constant="-60" id="vIO-x1-7Q2"/>
|
||||
<constraint firstItem="jXe-xz-9Sd" firstAttribute="baseline" secondItem="MPN-np-SbT" secondAttribute="baseline" id="wys-ML-2Q2"/>
|
||||
<constraint firstItem="Yaj-QY-7xV" firstAttribute="top" secondItem="O4B-Z2-XYi" secondAttribute="bottom" constant="6" symbolic="YES" id="zZg-M3-hSk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</box>
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database."; ObjectID = "lblDisclaimer"; */
|
||||
"lblDisclaimer.title" = "DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
/* Class = "NSTextFieldCell"; title = "© 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2021-2022 vChewing Project.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine.";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.";
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
"1.title" = "vChewing Preferences";
|
||||
"sZx-18-8dO.title" = "Debug Mode";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "Microsoft, Dachen, Wang, etc.";
|
||||
"7.title" = "ETen";
|
||||
"8.title" = "Hsu";
|
||||
"9.title" = "ETen26";
|
||||
"10.title" = "Hanyu Pinyin with Numeral Intonation";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "Enable Space key for calling candidate window";
|
||||
"12.title" = "BPMF Parser:";
|
||||
"126.title" = "Basic Layout:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"14.title" = "Choose the cursor position where you want to list possible candidates.";
|
||||
"16.title" = "Cursor in front of the phrase (like macOS built-in Zhuyin IME)";
|
||||
"17.title" = "Cursor at the rear of the phrase (like Microsoft New Phonetic)";
|
||||
"18.title" = "Radio";
|
||||
"1AW-xf-c2f.label" = "Keyboard";
|
||||
"20.title" = "Radio";
|
||||
"21.title" = "Horizontal";
|
||||
"22.title" = "Vertical";
|
||||
"shc-Nu-UsM.title" = "Show page buttons in candidate list";
|
||||
"24.title" = "Candidate List Layout:";
|
||||
"27F-8T-FkQ.title" = "Fake Seigyou (similar to JinYei)";
|
||||
"29.title" = "Candidate UI font size:";
|
||||
"2iG-Ic-gbl.label" = "Dictionary";
|
||||
"2pS-nv-te4.title" = "Choose which keys you prefer for selecting candidates.";
|
||||
"2Y6-Am-WM1.title" = "General Settings";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "Microsoft, Dachen, Wang, etc.";
|
||||
"62u-jY-BRh.title" = "Stop farting (when typed phonetic combination is invalid, etc.)";
|
||||
"7.title" = "ETen";
|
||||
"7fV-x8-WHQ.title" = "MiTAC";
|
||||
"8.title" = "Hsu";
|
||||
"9.title" = "ETen26";
|
||||
"92.title" = "OtherViews";
|
||||
"93.title" = "12";
|
||||
"94.title" = "14";
|
||||
|
@ -24,68 +35,58 @@
|
|||
"96.title" = "18";
|
||||
"98.title" = "24";
|
||||
"99.title" = "32";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "Enable Space key for calling candidate window";
|
||||
"126.title" = "Basic Layout:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"7fV-x8-WHQ.title" = "MiTAC";
|
||||
"27F-8T-FkQ.title" = "Fake Seigyou (similar to JinYei)";
|
||||
"1AW-xf-c2f.label" = "Keyboard";
|
||||
"2Y6-Am-WM1.title" = "General Settings";
|
||||
"2pS-nv-te4.title" = "Choose which keys you prefer for selecting candidates.";
|
||||
"62u-jY-BRh.title" = "Stop farting (when typed phonetic combination is invalid, etc.)";
|
||||
"9DS-Rc-TXq.title" = "UI language setting:";
|
||||
"akC-2g-ybz.title" = "Simplified Chinese";
|
||||
"ArK-Vk-OoT.title" = "Emulating select-candidate-per-character mode";
|
||||
"BSK-bH-Gct.title" = "Auto-convert traditional Chinese glyphs to KangXi characters";
|
||||
"eia-1F-Do0.title" = "Auto-convert traditional Chinese glyphs to JIS Shinjitai characters";
|
||||
"cf2-se-PDO.title" = "Dictionary and Language Models";
|
||||
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"dIN-TZ-67g.title" = "Space to +cycle candidates, Shift+Space to +cycle pages";
|
||||
"E1l-m8-xgb.title" = "Advanced Settings";
|
||||
"FSG-lN-CJO.title" = "English";
|
||||
"FnD-oH-El5.title" = "Selection Keys:";
|
||||
"GlJ-Ns-9eE.title" = "Auto-Select";
|
||||
"QUQ-oY-4Hc.label" = "General";
|
||||
"RQ6-MS-m4C.title" = "Choose your preferred keyboard layout and phonetic parser.";
|
||||
"RUG-ls-KyA.title" = "Push the cursor in front of the phrase after selection";
|
||||
"TXr-FF-ehw.title" = "Traditional Chinese";
|
||||
"Uyz-xL-TVN.title" = "Output Settings";
|
||||
"W24-T4-cg0.title" = "Enable CNS11643 Support (2022-04-27)";
|
||||
"Wvt-HE-LOv.title" = "Keyboard Layout";
|
||||
"Z9t-P0-BLF.title" = "Check for updates automatically";
|
||||
"ZEv-Q2-mYL.title" = "Change user interface language (will reboot the IME).";
|
||||
"akC-2g-ybz.title" = "Simplified Chinese";
|
||||
"eia-1F-Do0.title" = "Auto-convert traditional Chinese glyphs to JIS Shinjitai characters";
|
||||
"f2j-xD-4xK.title" = "Use ESC key to clear the entire input buffer";
|
||||
"f8i-69-zxm.title" = "Automatically reload user data files if changes detected";
|
||||
"FnD-oH-El5.title" = "Selection Keys:";
|
||||
"FSG-lN-CJO.title" = "English";
|
||||
"FVC-br-H57.title" = "Cycling Candidates";
|
||||
"GlJ-Ns-9eE.title" = "Auto-Select";
|
||||
"hSv-LJ-Cq3.title" = "Enable symbol input support (incl. certain emoji symbols)";
|
||||
"iRg-wx-Nx2.title" = "Change UI font size of candidate window for a better visual clarity.";
|
||||
"iWy-Nw-QKB.title" = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[0]" = "Item 1";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[1]" = "Item 2";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[2]" = "Item 3";
|
||||
"rVQ-Hx-cGi.title" = "Japanese";
|
||||
"cf2-se-PDO.title" = "Dictionary and Language Models";
|
||||
"xC5-yV-1W1.title" = "Choose your preferred layout of the candidate window.";
|
||||
"xrE-8T-WKO.label" = "Advanced";
|
||||
"2iG-Ic-gbl.label" = "Dictionary";
|
||||
"wQ9-px-b07.title" = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";
|
||||
"ueU-Rz-a1C.title" = "Choose the behavior of (Shift+)Tab key in the candidate window.";
|
||||
"s7u-Fm-dVg.title" = "Cycling Pages";
|
||||
"FVC-br-H57.title" = "Cycling Candidates";
|
||||
"Pg5-G9-pY5.title" = "Choose the behavior of (Shift+)Space key with candidates.";
|
||||
"XqL-rf-X6d.title" = "Space to +cycle pages, Shift+Space to +cycle candidates";
|
||||
"dIN-TZ-67g.title" = "Space to +cycle candidates, Shift+Space to +cycle pages";
|
||||
"hSv-LJ-Cq3.title" = "Enable symbol input support (incl. certain emoji symbols)";
|
||||
"wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid.";
|
||||
"wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer";
|
||||
"iWy-Nw-QKB.title" = "Output Hanyu-Pinyin in lieu of Zhuyin when Ctrl(+Alt)+CMD+Enter";
|
||||
"xjP-r7-GaK.title" = "Dachen 26 (libChewing)";
|
||||
"Parser11.title" = "Secondary Pinyin with Numeral Intonation";
|
||||
"Parser12.title" = "Yale Pinyin with Numeral Intonation";
|
||||
"Parser13.title" = "Hualuo Pinyin with Numeral Intonation";
|
||||
"Parser14.title" = "Universal Pinyin with Numeral Intonation";
|
||||
"Pg5-G9-pY5.title" = "Choose the behavior of (Shift+)Space key with candidates.";
|
||||
"QUQ-oY-4Hc.label" = "General";
|
||||
"RQ6-MS-m4C.title" = "Choose your preferred keyboard layout and phonetic parser.";
|
||||
"RUG-ls-KyA.title" = "Push the cursor in front of the phrase after selection";
|
||||
"rVQ-Hx-cGi.title" = "Japanese";
|
||||
"s7u-Fm-dVg.title" = "Cycling Pages";
|
||||
"shc-Nu-UsM.title" = "Show page buttons in candidate list";
|
||||
"sZx-18-8dO.title" = "Debug Mode";
|
||||
"TXr-FF-ehw.title" = "Traditional Chinese";
|
||||
"ueU-Rz-a1C.title" = "Choose the behavior of (Shift+)Tab key in the candidate window.";
|
||||
"Uyz-xL-TVN.title" = "Output Settings";
|
||||
"W24-T4-cg0.title" = "Enable CNS11643 Support (2022-04-27)";
|
||||
"wFR-zX-M8H.title" = "Show Hanyu-Pinyin in the inline composition buffer";
|
||||
"wN3-k3-b2a.title" = "Choose your desired user data folder path. Will be omitted if invalid.";
|
||||
"wQ9-px-b07.title" = "Apple Dynamic Bopomofo Basic Keyboard Layouts (Dachen & Eten Traditional) must match the Dachen parser in order to be functional.";
|
||||
"Wvt-HE-LOv.title" = "Keyboard Layout";
|
||||
"xC5-yV-1W1.title" = "Choose your preferred layout of the candidate window.";
|
||||
"xibKeyboardShortcuts.title" = "Keyboard Shortcuts";
|
||||
"xibUsingHotKeySCPC.title" = "Per-Char Select Mode";
|
||||
"xibLabelBufferLimit.title" = "Buffer Limit:";
|
||||
"xibUsingHotKeyAssociates.title" = "Per-Char Associated Phrases";
|
||||
"xibUsingHotKeyCNS.title" = "CNS11643 Mode";
|
||||
"xibUsingHotKeyKangXi.title" = "Force KangXi Writing";
|
||||
"xibUsingHotKeyJIS.title" = "JIS Shinjitai Output";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "Half-Width Punctuation Mode";
|
||||
"xibLabelBufferLimit.title" = "Buffer Limit:";
|
||||
"xibUsingHotKeyJIS.title" = "JIS Shinjitai Output";
|
||||
"xibUsingHotKeyKangXi.title" = "Force KangXi Writing";
|
||||
"xibUsingHotKeySCPC.title" = "Per-Char Select Mode";
|
||||
"xjP-r7-GaK.title" = "Dachen 26 (libChewing)";
|
||||
"XqL-rf-X6d.title" = "Space to +cycle pages, Shift+Space to +cycle candidates";
|
||||
"xrE-8T-WKO.label" = "Advanced";
|
||||
"Z9t-P0-BLF.title" = "Check for updates automatically";
|
||||
"ZEv-Q2-mYL.title" = "Change user interface language (will reboot the IME).";
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database."; ObjectID = "lblDisclaimer"; */
|
||||
"lblDisclaimer.title" = "免責事項:vChewing Project は、OpenVanilla と協力関係や提携関係にあるわけではなく、OpenVanilla が小麦注音プロジェクトに同梱した辞書データについて、vChewing Project は一切責任負い兼ねる。特定な地政学的・観念形態的な内容は、vChewing アプリの世界的な普及に妨害する恐れがあるため、vChewing 公式辞書データに不収録。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
/* Class = "NSTextFieldCell"; title = "© 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2021-2022 vChewing Project.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "鉄恨ボポモフォエンジン開発:Shiki Suen。\n入力状態管理システム開発:Zonble Yang。\nmacOS 版威注音の開発:Shiki Suen, Isaac Xen, Hiraku Wang, など。\n威注音語彙データの維持:Shiki Suen。\nMegrez 辞書処理エンジン:Shiki Suen(Lukhnos の Gramambular C++ エンジンを Swift で再開発したものである)。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "macOS 版威注音の開発:Shiki Suen, Isaac Xen, Hiraku Wang, など。\n威注音語彙データの維持:Shiki Suen。";
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
"1.title" = "威注音アプリ機能設定";
|
||||
"sZx-18-8dO.title" = "欠陥辿着モード";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "大千配列(Microsoft 標準・王安など)";
|
||||
"7.title" = "倚天伝統配列";
|
||||
"8.title" = "許氏国音自然配列";
|
||||
"9.title" = "倚天形忘れ配列 (26キー)";
|
||||
"10.title" = "漢語弁音(ローマ字+数字音調)";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "Space キーで入力候補を呼び出す";
|
||||
"12.title" = "注音配列:";
|
||||
"126.title" = "基礎キーボード:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM 配列";
|
||||
"14.title" = "カーソルはどこで入力候補を呼び出すかとご選択ください:";
|
||||
"16.title" = "単語の前で // macOS 内蔵注音入力のやり方";
|
||||
"17.title" = "単語の後で // Microsoft 新注音入力のやり方";
|
||||
"18.title" = "Radio";
|
||||
"1AW-xf-c2f.label" = "キーボード";
|
||||
"20.title" = "Radio";
|
||||
"21.title" = "横型陳列";
|
||||
"22.title" = "縦型陳列";
|
||||
"shc-Nu-UsM.title" = "ページボタンを表示";
|
||||
"24.title" = "入力候補陳列の仕様:";
|
||||
"27F-8T-FkQ.title" = "偽精業配列";
|
||||
"29.title" = "候補文字の字号:";
|
||||
"2iG-Ic-gbl.label" = "辞書";
|
||||
"2pS-nv-te4.title" = "お好きなる言選り用キー陣列をお選びください。";
|
||||
"2Y6-Am-WM1.title" = "全般設定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "大千配列(Microsoft 標準・王安など)";
|
||||
"62u-jY-BRh.title" = "マナーモード // 外すと入力間違の時に変な音が出る";
|
||||
"7.title" = "倚天伝統配列";
|
||||
"7fV-x8-WHQ.title" = "神通配列";
|
||||
"8.title" = "許氏国音自然配列";
|
||||
"9.title" = "倚天形忘れ配列 (26キー)";
|
||||
"92.title" = "OtherViews";
|
||||
"93.title" = "12";
|
||||
"94.title" = "14";
|
||||
|
@ -24,68 +35,58 @@
|
|||
"96.title" = "18";
|
||||
"98.title" = "24";
|
||||
"99.title" = "32";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "Space キーで入力候補を呼び出す";
|
||||
"126.title" = "基礎キーボード:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM 配列";
|
||||
"7fV-x8-WHQ.title" = "神通配列";
|
||||
"27F-8T-FkQ.title" = "偽精業配列";
|
||||
"1AW-xf-c2f.label" = "キーボード";
|
||||
"2Y6-Am-WM1.title" = "全般設定";
|
||||
"2pS-nv-te4.title" = "お好きなる言選り用キー陣列をお選びください。";
|
||||
"62u-jY-BRh.title" = "マナーモード // 外すと入力間違の時に変な音が出る";
|
||||
"9DS-Rc-TXq.title" = "アプリ表示用言語:";
|
||||
"akC-2g-ybz.title" = "簡体中国語";
|
||||
"ArK-Vk-OoT.title" = "漢字1つづつ全候補選択入力モード";
|
||||
"BSK-bH-Gct.title" = "入力した繁体字を康熙字体と自動変換";
|
||||
"eia-1F-Do0.title" = "入力した繁体字を日文 JIS 新字体と自動変換";
|
||||
"cf2-se-PDO.title" = "辞書と言語モデル";
|
||||
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "即排除/即最優先にできる候補の文字数の最低限は1字とする";
|
||||
"dIN-TZ-67g.title" = "Shift+Space で次のページ、Space で次の候補文字を";
|
||||
"E1l-m8-xgb.title" = "詳細設定";
|
||||
"FSG-lN-CJO.title" = "英語";
|
||||
"FnD-oH-El5.title" = "言選り用キー:";
|
||||
"GlJ-Ns-9eE.title" = "システム設定に準ずる";
|
||||
"QUQ-oY-4Hc.label" = "全般";
|
||||
"RQ6-MS-m4C.title" = "お好きなるキーボードとそれに相応しい注音配列をお選びください。";
|
||||
"RUG-ls-KyA.title" = "候補選択の直後、すぐカーソルを単語の向こうに推す";
|
||||
"TXr-FF-ehw.title" = "繁体中国語";
|
||||
"Uyz-xL-TVN.title" = "出力設定";
|
||||
"W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-04-27)";
|
||||
"Wvt-HE-LOv.title" = "キーボード";
|
||||
"Z9t-P0-BLF.title" = "アプリの更新通知を受く";
|
||||
"ZEv-Q2-mYL.title" = "アプリ表示用言語をご指定ください、そして入力アプリは自動的に再起動。";
|
||||
"akC-2g-ybz.title" = "簡体中国語";
|
||||
"eia-1F-Do0.title" = "入力した繁体字を日文 JIS 新字体と自動変換";
|
||||
"f2j-xD-4xK.title" = "ESC キーで入力緩衝列を消す";
|
||||
"f8i-69-zxm.title" = "変更されたユーザー辞書データを自動的に再読込";
|
||||
"FnD-oH-El5.title" = "言選り用キー:";
|
||||
"FSG-lN-CJO.title" = "英語";
|
||||
"FVC-br-H57.title" = "候補文字そのもの";
|
||||
"GlJ-Ns-9eE.title" = "システム設定に準ずる";
|
||||
"hSv-LJ-Cq3.title" = "僅かなる絵文字も含む符号入力サポートを起用";
|
||||
"iRg-wx-Nx2.title" = "入力候補陣列の候補文字の字号をご指定ください。";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter で出すのを漢語弁音と変換";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[0]" = "Item 1";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[1]" = "Item 2";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[2]" = "Item 3";
|
||||
"rVQ-Hx-cGi.title" = "和語";
|
||||
"cf2-se-PDO.title" = "辞書と言語モデル";
|
||||
"xC5-yV-1W1.title" = "入力候補陳列の仕様をご指定ください。";
|
||||
"xrE-8T-WKO.label" = "詳細";
|
||||
"2iG-Ic-gbl.label" = "辞書";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の注音配列を大千と設定すべきである。";
|
||||
"ueU-Rz-a1C.title" = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。";
|
||||
"s7u-Fm-dVg.title" = "ページ";
|
||||
"FVC-br-H57.title" = "候補文字そのもの";
|
||||
"Pg5-G9-pY5.title" = "入力候補についての (Shift+)Space キーの輪番切替対象をご指定ください。";
|
||||
"XqL-rf-X6d.title" = "Space で次のページ、Shift+Space で次の候補文字を";
|
||||
"dIN-TZ-67g.title" = "Shift+Space で次のページ、Space で次の候補文字を";
|
||||
"hSv-LJ-Cq3.title" = "僅かなる絵文字も含む符号入力サポートを起用";
|
||||
"wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。";
|
||||
"wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列で代わりに漢語弁音の音読み)";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter で出すのを漢語弁音と変換";
|
||||
"xjP-r7-GaK.title" = "酷音大千 26 キー配列";
|
||||
"Parser11.title" = "国音二式 (ローマ字+数字音調)";
|
||||
"Parser12.title" = "イェール弁音 (ローマ字+数字音調)";
|
||||
"Parser13.title" = "中華ローマ弁音 (ローマ字+数字音調)";
|
||||
"Parser14.title" = "汎用弁音 (ローマ字+数字音調)";
|
||||
"Pg5-G9-pY5.title" = "入力候補についての (Shift+)Space キーの輪番切替対象をご指定ください。";
|
||||
"QUQ-oY-4Hc.label" = "全般";
|
||||
"RQ6-MS-m4C.title" = "お好きなるキーボードとそれに相応しい注音配列をお選びください。";
|
||||
"RUG-ls-KyA.title" = "候補選択の直後、すぐカーソルを単語の向こうに推す";
|
||||
"rVQ-Hx-cGi.title" = "和語";
|
||||
"s7u-Fm-dVg.title" = "ページ";
|
||||
"shc-Nu-UsM.title" = "ページボタンを表示";
|
||||
"sZx-18-8dO.title" = "欠陥辿着モード";
|
||||
"TXr-FF-ehw.title" = "繁体中国語";
|
||||
"ueU-Rz-a1C.title" = "入力候補陳列での (Shift+)Tab キーの輪番切替対象をご指定ください。";
|
||||
"Uyz-xL-TVN.title" = "出力設定";
|
||||
"W24-T4-cg0.title" = "全字庫モード // 入力可能の漢字数倍増 (2022-04-27)";
|
||||
"wFR-zX-M8H.title" = "弁音合併入力(入力緩衝列で代わりに漢語弁音の音読み)";
|
||||
"wN3-k3-b2a.title" = "欲しがるユーザー辞書保存先をご指定ください。無効の保存先設定は効かぬ。";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音キーボード (大千と倚天伝統) を使うには、共通語分析器の注音配列を大千と設定すべきである。";
|
||||
"Wvt-HE-LOv.title" = "キーボード";
|
||||
"xC5-yV-1W1.title" = "入力候補陳列の仕様をご指定ください。";
|
||||
"xibKeyboardShortcuts.title" = "ショートカット";
|
||||
"xibUsingHotKeySCPC.title" = "全候補入力モード";
|
||||
"xibLabelBufferLimit.title" = "緩衝列の容量:";
|
||||
"xibUsingHotKeyAssociates.title" = "全候補入力で連想語彙";
|
||||
"xibUsingHotKeyCNS.title" = "全字庫モード";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙文字変換モード";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字体モード";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半角句読モード";
|
||||
"xibLabelBufferLimit.title" = "緩衝列の容量:";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字体モード";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙文字変換モード";
|
||||
"xibUsingHotKeySCPC.title" = "全候補入力モード";
|
||||
"xjP-r7-GaK.title" = "酷音大千 26 キー配列";
|
||||
"XqL-rf-X6d.title" = "Space で次のページ、Shift+Space で次の候補文字を";
|
||||
"xrE-8T-WKO.label" = "詳細";
|
||||
"Z9t-P0-BLF.title" = "アプリの更新通知を受く";
|
||||
"ZEv-Q2-mYL.title" = "アプリ表示用言語をご指定ください、そして入力アプリは自動的に再起動。";
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database."; ObjectID = "lblDisclaimer"; */
|
||||
"lblDisclaimer.title" = "免责声明:威注音专案对小麦注音官方专案内赠的小麦注音原版词库内容不负任何责任。威注音输入法专用的威注音官方词库不包含任何「会在法理上妨碍威注音在全球传播」的「与地缘政治及政治意识形态有关的」内容。威注音专案与 OpenVanilla 专案之间无合作关系、无隶属关系。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
/* Class = "NSTextFieldCell"; title = "© 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2021-2022 vChewing Project.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "铁恨注音并击输入处理引擎研发:Shiki Suen。\n输入法状态管理引擎研发:Zonble Yang。\n威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。\n天权星语汇引擎:Shiki Suen,用 Swift 将 Lukhnos 的 C++ Gramambular 重写而得。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。";
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
"1.title" = "威注音偏好设定";
|
||||
"sZx-18-8dO.title" = "侦错模式";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微软/大千/王安/国乔/零壹/仲鼎";
|
||||
"7.title" = "倚天传统";
|
||||
"8.title" = "许氏(国音&自然)";
|
||||
"9.title" = "倚天二十六键";
|
||||
"10.title" = "汉语拼音+数字标调";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "敲空格键以选字";
|
||||
"12.title" = "基础键盘布局:";
|
||||
"126.title" = "英数键盘布局:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"14.title" = "用以触发选字的光标相对位置:";
|
||||
"16.title" = "光标置于词语前方 // macOS 内建注音风格";
|
||||
"17.title" = "光标置于词语后方 // Windows 微软新注音风格";
|
||||
"18.title" = "Radio";
|
||||
"1AW-xf-c2f.label" = "键盘";
|
||||
"20.title" = "Radio";
|
||||
"21.title" = "横向布局";
|
||||
"22.title" = "纵向布局";
|
||||
"shc-Nu-UsM.title" = "在选字窗内显示翻页按钮";
|
||||
"24.title" = "候选字窗布局:";
|
||||
"27F-8T-FkQ.title" = "伪精业";
|
||||
"29.title" = "字型大小设定:";
|
||||
"2iG-Ic-gbl.label" = "辞典";
|
||||
"2pS-nv-te4.title" = "选择您所偏好的用来选字的按键组合。";
|
||||
"2Y6-Am-WM1.title" = "一般设定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微软/大千/王安/国乔/零壹/仲鼎";
|
||||
"62u-jY-BRh.title" = "廉耻模式 // 取消勾选的话,敲错字时会有异音";
|
||||
"7.title" = "倚天传统";
|
||||
"7fV-x8-WHQ.title" = "神通";
|
||||
"8.title" = "许氏(国音&自然)";
|
||||
"9.title" = "倚天二十六键";
|
||||
"92.title" = "OtherViews";
|
||||
"93.title" = "12";
|
||||
"94.title" = "14";
|
||||
|
@ -24,68 +35,59 @@
|
|||
"96.title" = "18";
|
||||
"98.title" = "24";
|
||||
"99.title" = "32";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "敲空格键以选字";
|
||||
"126.title" = "英数键盘布局:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"7fV-x8-WHQ.title" = "神通";
|
||||
"27F-8T-FkQ.title" = "伪精业";
|
||||
"1AW-xf-c2f.label" = "键盘";
|
||||
"2Y6-Am-WM1.title" = "一般设定";
|
||||
"2pS-nv-te4.title" = "选择您所偏好的用来选字的按键组合。";
|
||||
"62u-jY-BRh.title" = "廉耻模式 // 取消勾选的话,敲错字时会有异音";
|
||||
"9DS-Rc-TXq.title" = "接口语言设定:";
|
||||
"akC-2g-ybz.title" = "简体中文";
|
||||
"ArK-Vk-OoT.title" = "仿真 90 年代前期注音逐字选字输入风格";
|
||||
"BSK-bH-Gct.title" = "自动将繁体中文字转换为康熙正体字";
|
||||
"eia-1F-Do0.title" = "自动将繁体中文字转换为日本简化字(JIS 新字体)";
|
||||
"cf2-se-PDO.title" = "辞典&語言模型";
|
||||
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "将可以就地升权/排除的候选字词的最短词长设为单个汉字";
|
||||
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "Allow boosting / excluding a candidate of single kanji";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 换下一页,Space 换选下一个候选字。";
|
||||
"E1l-m8-xgb.title" = "进阶设定";
|
||||
"FSG-lN-CJO.title" = "英文";
|
||||
"FnD-oH-El5.title" = "选字键:";
|
||||
"GlJ-Ns-9eE.title" = "自动选择";
|
||||
"QUQ-oY-4Hc.label" = "一般";
|
||||
"RQ6-MS-m4C.title" = "选择您所偏好的系统键盘布局与注音分析器排列。";
|
||||
"RUG-ls-KyA.title" = "在选字后将光标置于该字词的前方";
|
||||
"TXr-FF-ehw.title" = "繁体中文";
|
||||
"Uyz-xL-TVN.title" = "输出设定";
|
||||
"W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-04-27)";
|
||||
"Wvt-HE-LOv.title" = "键盘布局";
|
||||
"Z9t-P0-BLF.title" = "自动检查软件更新";
|
||||
"ZEv-Q2-mYL.title" = "变更使用者接口语言,会自动重新启动输入法。";
|
||||
"akC-2g-ybz.title" = "简体中文";
|
||||
"eia-1F-Do0.title" = "自动将繁体中文字转换为日本简化字(JIS 新字体)";
|
||||
"f2j-xD-4xK.title" = "敲 ESC 键以清空整个输入缓冲区";
|
||||
"f8i-69-zxm.title" = "自动重新加载变更过的使用者资料内容";
|
||||
"FnD-oH-El5.title" = "选字键:";
|
||||
"FSG-lN-CJO.title" = "英文";
|
||||
"FVC-br-H57.title" = "轮替候选字";
|
||||
"GlJ-Ns-9eE.title" = "自动选择";
|
||||
"hSv-LJ-Cq3.title" = "启用包括少许绘文字在内的符号输入支援";
|
||||
"iRg-wx-Nx2.title" = "变更候选字窗的字型大小。";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 输出汉语拼音而非注音";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[0]" = "Item 1";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[1]" = "Item 2";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[2]" = "Item 3";
|
||||
"rVQ-Hx-cGi.title" = "和语";
|
||||
"cf2-se-PDO.title" = "辞典&語言模型";
|
||||
"xC5-yV-1W1.title" = "选择您所偏好的候选字窗布局。";
|
||||
"xrE-8T-WKO.label" = "进阶";
|
||||
"2iG-Ic-gbl.label" = "辞典";
|
||||
"wQ9-px-b07.title" = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器的注音排列得配置为大千排列。";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 热键在选字窗内的轮替操作对象。";
|
||||
"s7u-Fm-dVg.title" = "轮替页面";
|
||||
"FVC-br-H57.title" = "轮替候选字";
|
||||
"Pg5-G9-pY5.title" = "指定 (Shift+)Space 热键对候选字词而言的轮替操作对象。";
|
||||
"XqL-rf-X6d.title" = "Space 换下一页,Shift+Space 换选下一个候选字。";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 换下一页,Space 换选下一个候选字。";
|
||||
"hSv-LJ-Cq3.title" = "启用包括少许绘文字在内的符号输入支援";
|
||||
"wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。";
|
||||
"wFR-zX-M8H.title" = "拼音并击模式(组字区内看到的是汉语拼音)";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 输出汉语拼音而非注音";
|
||||
"xjP-r7-GaK.title" = "酷音大千二十六键";
|
||||
"Parser11.title" = "国音二式+数字标调";
|
||||
"Parser12.title" = "耶鲁拼音+数字标调";
|
||||
"Parser13.title" = "华罗拼音+数字标调";
|
||||
"Parser14.title" = "通用拼音+数字标调";
|
||||
"Pg5-G9-pY5.title" = "指定 (Shift+)Space 热键对候选字词而言的轮替操作对象。";
|
||||
"QUQ-oY-4Hc.label" = "一般";
|
||||
"RQ6-MS-m4C.title" = "选择您所偏好的系统键盘布局与注音分析器排列。";
|
||||
"RUG-ls-KyA.title" = "在选字后将光标置于该字词的前方";
|
||||
"rVQ-Hx-cGi.title" = "和语";
|
||||
"s7u-Fm-dVg.title" = "轮替页面";
|
||||
"shc-Nu-UsM.title" = "在选字窗内显示翻页按钮";
|
||||
"sZx-18-8dO.title" = "侦错模式";
|
||||
"TXr-FF-ehw.title" = "繁体中文";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 热键在选字窗内的轮替操作对象。";
|
||||
"Uyz-xL-TVN.title" = "输出设定";
|
||||
"W24-T4-cg0.title" = "启用 CNS11643 全字库支援 (2022-04-27)";
|
||||
"wFR-zX-M8H.title" = "拼音并击模式(组字区内看到的是汉语拼音)";
|
||||
"wN3-k3-b2a.title" = "请在此指定您想指定的使用者语汇档案目录。无效值会被忽略。";
|
||||
"wQ9-px-b07.title" = "Apple 动态注音键盘布局(大千与倚天)要求普通话/国音分析器的注音排列得配置为大千排列。";
|
||||
"Wvt-HE-LOv.title" = "键盘布局";
|
||||
"xC5-yV-1W1.title" = "选择您所偏好的候选字窗布局。";
|
||||
"xibKeyboardShortcuts.title" = "键盘快捷键";
|
||||
"xibUsingHotKeySCPC.title" = "模拟逐字选字输入";
|
||||
"xibLabelBufferLimit.title" = "组字区容量:";
|
||||
"xibUsingHotKeyAssociates.title" = "逐字选字联想模式";
|
||||
"xibUsingHotKeyCNS.title" = "全字库模式";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙正体字模式";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字体模式";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半形标点模式";
|
||||
"xibLabelBufferLimit.title" = "组字区容量:";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字体模式";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙正体字模式";
|
||||
"xibUsingHotKeySCPC.title" = "模拟逐字选字输入";
|
||||
"xjP-r7-GaK.title" = "酷音大千二十六键";
|
||||
"XqL-rf-X6d.title" = "Space 换下一页,Shift+Space 换选下一个候选字。";
|
||||
"xrE-8T-WKO.label" = "进阶";
|
||||
"Z9t-P0-BLF.title" = "自动检查软件更新";
|
||||
"ZEv-Q2-mYL.title" = "变更使用者接口语言,会自动重新启动输入法。";
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
/* Class = "NSTextFieldCell"; title = "DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database."; ObjectID = "lblDisclaimer"; */
|
||||
"lblDisclaimer.title" = "免責聲明:威注音專案對小麥注音官方專案內贈的小麥注音原版詞庫內容不負任何責任。威注音輸入法專用的威注音官方詞庫不包含任何「會在法理上妨礙威注音在全球傳播」的「與地緣政治及政治意識形態有關的」內容。威註音專案與 OpenVanilla 專案之間無合作關係、無隸屬關係。";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2011-2022 OpenVanilla Project & © 2021-2022 vChewing Project.";
|
||||
/* Class = "NSTextFieldCell"; title = "© 2021-2022 vChewing Project."; ObjectID = "lblCopyright"; */
|
||||
// "lblCopyright.title" = "© 2021-2022 vChewing Project.";
|
||||
|
||||
/* Class = "NSTextFieldCell"; title = "Tekkon Syllable Composition Engine by Shiki Suen.\nInput State Management Architecture by Zonble Yang.\nvChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen.\nMegrez is a rewritten unigram engine by Shiki Suen using Swift, replacing Lukhnos' C++ Gramambular engine."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "鐵恨注音並擊輸入處理引擎研發:Shiki Suen。\n輸入法狀態管理引擎研發:Zonble Yang。\n威注音 macOS 程式研發:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音詞庫維護:Shiki Suen。\n天權星語彙引擎:Shiki Suen,用 Swift 將 Lukhnos 的 C++ Gramambular 重寫而得。";
|
||||
/* Class = "NSTextFieldCell"; title = "vChewing macOS Development: Shiki Suen, Isaac Xen, Hiraku Wang, etc.\nvChewing Phrase Database Maintained by Shiki Suen."; ObjectID = "lblCredits"; */
|
||||
"lblCredits.title" = "威注音 macOS 程式研发:Shiki Suen, Isaac Xen, Hiraku Wang, 等。\n威注音词库维护:Shiki Suen。";
|
||||
|
|
|
@ -1,22 +1,33 @@
|
|||
"1.title" = "威注音偏好設定";
|
||||
"sZx-18-8dO.title" = "偵錯模式";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微軟/大千/王安/國喬/零壹/仲鼎";
|
||||
"7.title" = "倚天傳統";
|
||||
"8.title" = "許氏(國音&自然)";
|
||||
"9.title" = "倚天二十六鍵";
|
||||
"10.title" = "漢語拼音+數字標調";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "敲空格鍵以選字";
|
||||
"12.title" = "注音排列:";
|
||||
"126.title" = "基礎鍵盤佈局:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"14.title" = "用以觸發選字的游標相對位置:";
|
||||
"16.title" = "游標置於詞語前方 // macOS 內建注音風格";
|
||||
"17.title" = "游標置於詞語後方 // Windows 微軟新注音風格";
|
||||
"18.title" = "Radio";
|
||||
"1AW-xf-c2f.label" = "鍵盤";
|
||||
"20.title" = "Radio";
|
||||
"21.title" = "橫向佈局";
|
||||
"22.title" = "縱向佈局";
|
||||
"shc-Nu-UsM.title" = "在選字窗內顯示翻頁按鈕";
|
||||
"24.title" = "候選字窗佈局:";
|
||||
"27F-8T-FkQ.title" = "偽精業";
|
||||
"29.title" = "字型大小設定:";
|
||||
"2iG-Ic-gbl.label" = "辭典";
|
||||
"2pS-nv-te4.title" = "選擇您所偏好的用來選字的按鍵組合。";
|
||||
"2Y6-Am-WM1.title" = "一般設定";
|
||||
"5.title" = "OtherViews";
|
||||
"6.title" = "微軟/大千/王安/國喬/零壹/仲鼎";
|
||||
"62u-jY-BRh.title" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音";
|
||||
"7.title" = "倚天傳統";
|
||||
"7fV-x8-WHQ.title" = "神通";
|
||||
"8.title" = "許氏(國音&自然)";
|
||||
"9.title" = "倚天二十六鍵";
|
||||
"92.title" = "OtherViews";
|
||||
"93.title" = "12";
|
||||
"94.title" = "14";
|
||||
|
@ -24,68 +35,58 @@
|
|||
"96.title" = "18";
|
||||
"98.title" = "24";
|
||||
"99.title" = "32";
|
||||
"100.title" = "64";
|
||||
"101.title" = "96";
|
||||
"110.title" = "敲空格鍵以選字";
|
||||
"126.title" = "基礎鍵盤佈局:";
|
||||
"128.title" = "OtherViews";
|
||||
"137.title" = "IBM";
|
||||
"7fV-x8-WHQ.title" = "神通";
|
||||
"27F-8T-FkQ.title" = "偽精業";
|
||||
"1AW-xf-c2f.label" = "鍵盤";
|
||||
"2Y6-Am-WM1.title" = "一般設定";
|
||||
"2pS-nv-te4.title" = "選擇您所偏好的用來選字的按鍵組合。";
|
||||
"62u-jY-BRh.title" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音";
|
||||
"9DS-Rc-TXq.title" = "介面語言設定:";
|
||||
"akC-2g-ybz.title" = "簡體中文";
|
||||
"ArK-Vk-OoT.title" = "模擬 90 年代前期注音逐字選字輸入風格";
|
||||
"BSK-bH-Gct.title" = "自動將繁體中文字轉換為康熙正體字";
|
||||
"eia-1F-Do0.title" = "自動將繁體中文字轉換為日本簡化字(JIS 新字體)";
|
||||
"cf2-se-PDO.title" = "辭典&語言模型";
|
||||
"chkAllowBoostingSingleKanjiAsUserPhrase.title" = "將可以就地升權/排除的候選字詞的最短詞長設為單個漢字";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 換下一頁,Space 換選下一個候選字";
|
||||
"E1l-m8-xgb.title" = "進階設定";
|
||||
"FSG-lN-CJO.title" = "英文";
|
||||
"FnD-oH-El5.title" = "選字鍵:";
|
||||
"GlJ-Ns-9eE.title" = "自動選擇";
|
||||
"QUQ-oY-4Hc.label" = "一般";
|
||||
"RQ6-MS-m4C.title" = "選擇您所偏好的系統鍵盤佈局與注音分析器排列。";
|
||||
"RUG-ls-KyA.title" = "在選字後將游標置於該字詞的前方";
|
||||
"TXr-FF-ehw.title" = "繁體中文";
|
||||
"Uyz-xL-TVN.title" = "輸出設定";
|
||||
"W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-04-27)";
|
||||
"Wvt-HE-LOv.title" = "鍵盤佈局";
|
||||
"Z9t-P0-BLF.title" = "自動檢查軟體更新";
|
||||
"ZEv-Q2-mYL.title" = "變更使用者介面語言,會自動重新啟動輸入法。";
|
||||
"akC-2g-ybz.title" = "簡體中文";
|
||||
"eia-1F-Do0.title" = "自動將繁體中文字轉換為日本簡化字(JIS 新字體)";
|
||||
"f2j-xD-4xK.title" = "敲 ESC 鍵以清空整個輸入緩衝區";
|
||||
"f8i-69-zxm.title" = "自動重新載入變更過的使用者數據內容";
|
||||
"FnD-oH-El5.title" = "選字鍵:";
|
||||
"FSG-lN-CJO.title" = "英文";
|
||||
"FVC-br-H57.title" = "輪替候選字";
|
||||
"GlJ-Ns-9eE.title" = "自動選擇";
|
||||
"hSv-LJ-Cq3.title" = "啟用包括少許繪文字在內的符號輸入支援";
|
||||
"iRg-wx-Nx2.title" = "變更候選字窗的字型大小。";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 輸出漢語拼音而非注音";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[0]" = "Item 1";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[1]" = "Item 2";
|
||||
"jQC-12-UuK.ibShadowedObjectValues[2]" = "Item 3";
|
||||
"rVQ-Hx-cGi.title" = "和語";
|
||||
"cf2-se-PDO.title" = "辭典&語言模型";
|
||||
"xC5-yV-1W1.title" = "選擇您所偏好的候選字窗佈局。";
|
||||
"xrE-8T-WKO.label" = "進階";
|
||||
"2iG-Ic-gbl.label" = "辭典";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器的注音排列得配置為大千排列。";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 熱鍵在選字窗內的輪替操作對象。";
|
||||
"s7u-Fm-dVg.title" = "輪替頁面";
|
||||
"FVC-br-H57.title" = "輪替候選字";
|
||||
"Pg5-G9-pY5.title" = "指定 (Shift+)Space 熱鍵對候選字詞而言的輪替操作對象。";
|
||||
"XqL-rf-X6d.title" = "Space 換下一頁,Shift+Space 換選下一個候選字";
|
||||
"dIN-TZ-67g.title" = "Shift+Space 換下一頁,Space 換選下一個候選字";
|
||||
"hSv-LJ-Cq3.title" = "啟用包括少許繪文字在內的符號輸入支援";
|
||||
"wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。";
|
||||
"wFR-zX-M8H.title" = "拼音並擊模式(組字區內看到的是漢語拼音)";
|
||||
"iWy-Nw-QKB.title" = "Ctrl(+Alt)+CMD+Enter 輸出漢語拼音而非注音";
|
||||
"xjP-r7-GaK.title" = "酷音大千二十六鍵";
|
||||
"Parser11.title" = "國音二式+數字標調";
|
||||
"Parser12.title" = "耶魯拼音+數字標調";
|
||||
"Parser13.title" = "華羅拼音+數字標調";
|
||||
"Parser14.title" = "通用拼音+數字標調";
|
||||
"Pg5-G9-pY5.title" = "指定 (Shift+)Space 熱鍵對候選字詞而言的輪替操作對象。";
|
||||
"QUQ-oY-4Hc.label" = "一般";
|
||||
"RQ6-MS-m4C.title" = "選擇您所偏好的系統鍵盤佈局與注音分析器排列。";
|
||||
"RUG-ls-KyA.title" = "在選字後將游標置於該字詞的前方";
|
||||
"rVQ-Hx-cGi.title" = "和語";
|
||||
"s7u-Fm-dVg.title" = "輪替頁面";
|
||||
"shc-Nu-UsM.title" = "在選字窗內顯示翻頁按鈕";
|
||||
"sZx-18-8dO.title" = "偵錯模式";
|
||||
"TXr-FF-ehw.title" = "繁體中文";
|
||||
"ueU-Rz-a1C.title" = "指定 (Shift+)Tab 熱鍵在選字窗內的輪替操作對象。";
|
||||
"Uyz-xL-TVN.title" = "輸出設定";
|
||||
"W24-T4-cg0.title" = "啟用 CNS11643 全字庫支援 (2022-04-27)";
|
||||
"wFR-zX-M8H.title" = "拼音並擊模式(組字區內看到的是漢語拼音)";
|
||||
"wN3-k3-b2a.title" = "請在此指定您想指定的使用者語彙檔案目錄。無效值會被忽略。";
|
||||
"wQ9-px-b07.title" = "Apple 動態注音鍵盤佈局(大千與倚天)要求普通話/國音分析器的注音排列得配置為大千排列。";
|
||||
"Wvt-HE-LOv.title" = "鍵盤佈局";
|
||||
"xC5-yV-1W1.title" = "選擇您所偏好的候選字窗佈局。";
|
||||
"xibKeyboardShortcuts.title" = "鍵盤快速鍵";
|
||||
"xibUsingHotKeySCPC.title" = "模擬逐字選字輸入";
|
||||
"xibLabelBufferLimit.title" = "組字區容量:";
|
||||
"xibUsingHotKeyAssociates.title" = "逐字選字聯想模式";
|
||||
"xibUsingHotKeyCNS.title" = "全字庫模式";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙正體字模式";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字體模式";
|
||||
"xibUsingHotKeyHalfWidthASCII.title" = "半形標點模式";
|
||||
"xibLabelBufferLimit.title" = "組字區容量:";
|
||||
"xibUsingHotKeyJIS.title" = "JIS 新字體模式";
|
||||
"xibUsingHotKeyKangXi.title" = "康熙正體字模式";
|
||||
"xibUsingHotKeySCPC.title" = "模擬逐字選字輸入";
|
||||
"xjP-r7-GaK.title" = "酷音大千二十六鍵";
|
||||
"XqL-rf-X6d.title" = "Space 換下一頁,Shift+Space 換選下一個候選字";
|
||||
"xrE-8T-WKO.label" = "進階";
|
||||
"Z9t-P0-BLF.title" = "自動檢查軟體更新";
|
||||
"ZEv-Q2-mYL.title" = "變更使用者介面語言,會自動重新啟動輸入法。";
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.6.3</string>
|
||||
<string>1.7.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1963</string>
|
||||
<string>1970</string>
|
||||
<key>UpdateInfoEndpoint</key>
|
||||
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
|
||||
<key>UpdateInfoSite</key>
|
||||
|
|
|
@ -726,7 +726,7 @@
|
|||
<key>USE_HFS+_COMPRESSION</key>
|
||||
<false/>
|
||||
<key>VERSION</key>
|
||||
<string>1.6.3</string>
|
||||
<string>1.7.0</string>
|
||||
</dict>
|
||||
<key>TYPE</key>
|
||||
<integer>0</integer>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; };
|
||||
5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; };
|
||||
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */; };
|
||||
5B3133BF280B229700A4A505 /* KeyHandler_States.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B3133BE280B229700A4A505 /* KeyHandler_States.swift */; };
|
||||
5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1D15FC0EB100ABF4B3 /* 6_Unigram.swift */; };
|
||||
5B38F59B281E2E49007D5F5D /* 7_KeyValuePair.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */; };
|
||||
|
@ -28,11 +29,9 @@
|
|||
5B62A32927AE77D100A19448 /* FSEventStreamHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */; };
|
||||
5B62A33227AE792F00A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
|
||||
5B62A33627AE795800A19448 /* mgrPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33527AE795800A19448 /* mgrPrefs.swift */; };
|
||||
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33727AE79CD00A19448 /* NSStringUtils.swift */; };
|
||||
5B62A33827AE79CD00A19448 /* StringUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33727AE79CD00A19448 /* StringUtils.swift */; };
|
||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
||||
5B62A34627AE7CD900A19448 /* ctlCandidateHorizontal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33F27AE7CD900A19448 /* ctlCandidateHorizontal.swift */; };
|
||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
|
||||
5B62A34827AE7CD900A19448 /* ctlCandidateVertical.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34127AE7CD900A19448 /* ctlCandidateVertical.swift */; };
|
||||
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34327AE7CD900A19448 /* TooltipController.swift */; };
|
||||
5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34527AE7CD900A19448 /* NotifierController.swift */; };
|
||||
5B62A35327AE89C400A19448 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33127AE792F00A19448 /* InputSourceHelper.swift */; };
|
||||
|
@ -191,6 +190,7 @@
|
|||
5B18BA7227C7BD8B0056EB19 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
|
||||
5B18BA7327C7BD8C0056EB19 /* LICENSE-JPN.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-JPN.txt"; sourceTree = "<group>"; };
|
||||
5B18BA7427C7BD8C0056EB19 /* LICENSE-CHT.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = "LICENSE-CHT.txt"; sourceTree = "<group>"; };
|
||||
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlCandidateUniversal.swift; sourceTree = "<group>"; };
|
||||
5B2DB17127AF8771006D874E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; name = Makefile; path = Data/Makefile; sourceTree = "<group>"; };
|
||||
5B30F11227BA568800484E24 /* vChewingKeyLayout.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = vChewingKeyLayout.bundle; sourceTree = "<group>"; };
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = KeyHandler_States.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
|
@ -202,13 +202,12 @@
|
|||
5B62A32827AE77D100A19448 /* FSEventStreamHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = FSEventStreamHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33127AE792F00A19448 /* InputSourceHelper.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputSourceHelper.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33527AE795800A19448 /* mgrPrefs.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = mgrPrefs.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33727AE79CD00A19448 /* NSStringUtils.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NSStringUtils.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33727AE79CD00A19448 /* StringUtils.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringUtils.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A33F27AE7CD900A19448 /* ctlCandidateHorizontal.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidateHorizontal.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A34127AE7CD900A19448 /* ctlCandidateVertical.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidateVertical.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A34327AE7CD900A19448 /* TooltipController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = TooltipController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NotifierController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B65B919284D0185007C558B /* README-CHT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHT.md"; sourceTree = "<group>"; };
|
||||
5B707CE527D9F3A10099EF99 /* SwiftyOpenCC */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SwiftyOpenCC; path = Packages/SwiftyOpenCC; sourceTree = "<group>"; };
|
||||
5B707CE727D9F4590099EF99 /* OpenCCBridge.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = OpenCCBridge.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -373,6 +372,7 @@
|
|||
5B18BA7327C7BD8C0056EB19 /* LICENSE-JPN.txt */,
|
||||
5B18BA7227C7BD8B0056EB19 /* LICENSE.txt */,
|
||||
5B18BA7127C7BD8B0056EB19 /* README.md */,
|
||||
5B65B919284D0185007C558B /* README-CHT.md */,
|
||||
);
|
||||
name = MiscRootFiles;
|
||||
sourceTree = "<group>";
|
||||
|
@ -421,7 +421,7 @@
|
|||
5B7F225C2808501000DDD3CB /* KeyHandler_HandleInput.swift */,
|
||||
5B61B0C9280BEFD4002E3CFA /* KeyHandler_Misc.swift */,
|
||||
5B3133BE280B229700A4A505 /* KeyHandler_States.swift */,
|
||||
5B62A33727AE79CD00A19448 /* NSStringUtils.swift */,
|
||||
5B62A33727AE79CD00A19448 /* StringUtils.swift */,
|
||||
5BAA8FBD282CAF380066C406 /* SyllableComposer.swift */,
|
||||
5BF8423027BAA942008E7E4C /* vChewingKanjiConverter.swift */,
|
||||
);
|
||||
|
@ -526,8 +526,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */,
|
||||
5B62A33F27AE7CD900A19448 /* ctlCandidateHorizontal.swift */,
|
||||
5B62A34127AE7CD900A19448 /* ctlCandidateVertical.swift */,
|
||||
5B242402284B0D6500520FE4 /* ctlCandidateUniversal.swift */,
|
||||
);
|
||||
path = CandidateUI;
|
||||
sourceTree = "<group>";
|
||||
|
@ -896,7 +895,7 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1320;
|
||||
LastUpgradeCheck = 1330;
|
||||
LastUpgradeCheck = 1340;
|
||||
TargetAttributes = {
|
||||
5BD05BB727B2A429004C4F1D = {
|
||||
CreatedOnToolsVersion = 13.2;
|
||||
|
@ -1074,10 +1073,11 @@
|
|||
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */,
|
||||
5BAEFAD028012565001F42C9 /* mgrLangModel.swift in Sources */,
|
||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */,
|
||||
5B62A33827AE79CD00A19448 /* NSStringUtils.swift in Sources */,
|
||||
5B62A33827AE79CD00A19448 /* StringUtils.swift in Sources */,
|
||||
5BA9FD0F27FEDB6B002DE248 /* suiPrefPaneGeneral.swift in Sources */,
|
||||
5BA9FD4927FEF3C9002DE248 /* Section.swift in Sources */,
|
||||
5BA9FD3E27FEF3C8002DE248 /* Utilities.swift in Sources */,
|
||||
5B242403284B0D6500520FE4 /* ctlCandidateUniversal.swift in Sources */,
|
||||
5BA9FD1127FEDB6B002DE248 /* ctlPrefUI.swift in Sources */,
|
||||
5B38F59C281E2E49007D5F5D /* 2_Grid.swift in Sources */,
|
||||
5B40730D281672610023DFFF /* lmReplacements.swift in Sources */,
|
||||
|
@ -1087,7 +1087,6 @@
|
|||
5B62A34927AE7CD900A19448 /* TooltipController.swift in Sources */,
|
||||
5B61B0CA280BEFD4002E3CFA /* KeyHandler_Misc.swift in Sources */,
|
||||
5B38F59A281E2E49007D5F5D /* 6_Unigram.swift in Sources */,
|
||||
5B62A34827AE7CD900A19448 /* ctlCandidateVertical.swift in Sources */,
|
||||
5BA9FD4027FEF3C8002DE248 /* Localization.swift in Sources */,
|
||||
5BAA8FBE282CAF380066C406 /* SyllableComposer.swift in Sources */,
|
||||
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
||||
|
@ -1096,7 +1095,6 @@
|
|||
5BF8423127BAA942008E7E4C /* vChewingKanjiConverter.swift in Sources */,
|
||||
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */,
|
||||
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */,
|
||||
5B62A34627AE7CD900A19448 /* ctlCandidateHorizontal.swift in Sources */,
|
||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
|
||||
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
|
||||
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
|
||||
|
@ -1296,7 +1294,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -1319,7 +1317,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1352,7 +1350,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
|
@ -1371,7 +1369,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor;
|
||||
|
@ -1407,6 +1405,7 @@
|
|||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
|
@ -1449,6 +1448,7 @@
|
|||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -1486,7 +1486,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1521,7 +1521,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1553,7 +1553,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
|
@ -1583,11 +1583,12 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_COMPILATION_MODE = singlefile;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Source/Headers/vChewing-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
WRAPPER_EXTENSION = app;
|
||||
|
@ -1666,7 +1667,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -1691,7 +1692,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@ -1718,7 +1719,7 @@
|
|||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1963;
|
||||
CURRENT_PROJECT_VERSION = 1970;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
@ -1738,7 +1739,7 @@
|
|||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.11.5;
|
||||
MARKETING_VERSION = 1.6.3;
|
||||
MARKETING_VERSION = 1.7.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.atelierInmu.vChewing.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
LastUpgradeVersion = "1340"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
Loading…
Reference in New Issue