From e5e6c6f626c1aa52415506b87f41d822d70bd5fd Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 29 Oct 2022 22:21:33 +0200 Subject: [PATCH 001/164] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 295185f58..c51c76f44 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ package/test-* *~ .*.swp .DS_Store +rime-with-plugins-1.7.3-osx.zip From ec482ca7be07dd3cea7e9d03897352e491b97985 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 5 Dec 2022 07:22:33 +0100 Subject: [PATCH 002/164] Update plum --- plum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plum b/plum index ff888cbb9..3d06432dd 160000 --- a/plum +++ b/plum @@ -1 +1 @@ -Subproject commit ff888cbb9fce8c3f5b8b355baeb10685b2052b43 +Subproject commit 3d06432dda5fd1738bebda298c574b6ed2d512d8 From ce68080057f954aedbe76d901bdfac044daaf15f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Dec 2022 03:08:26 +0100 Subject: [PATCH 003/164] Distinguish Left/Right Modifiers --- SquirrelInputController.m | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index ebeec1bb2..05873a8a2 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -79,6 +79,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); + int keyCode = event.keyCode; int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); int release_mask = 0; NSUInteger changes = _lastModifier ^ modifiers; @@ -91,22 +92,35 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } if (changes & OSX_SHIFT_MASK) { release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask; - [self processKey:XK_Shift_L - modifiers:(rime_modifiers | release_mask)]; + if (keyCode == 0x38) { + [self processKey:XK_Shift_L modifiers:(rime_modifiers | release_mask)]; + } else if (keyCode == 0x3c) { + [self processKey:XK_Shift_R modifiers:(rime_modifiers | release_mask)]; + } } if (changes & OSX_CTRL_MASK) { release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask; - [self processKey:XK_Control_L - modifiers:(rime_modifiers | release_mask)]; + if (keyCode == 0x3b) { + [self processKey:XK_Control_L modifiers:(rime_modifiers | release_mask)]; + } else if (keyCode == 0x3e) { + [self processKey:XK_Control_R modifiers:(rime_modifiers | release_mask)]; + } } if (changes & OSX_ALT_MASK) { release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask; - [self processKey:XK_Alt_L modifiers:(rime_modifiers | release_mask)]; + if (keyCode == 0x3a) { + [self processKey:XK_Alt_L modifiers:(rime_modifiers | release_mask)]; + } else if (keyCode == 0x3d) { + [self processKey:XK_Alt_R modifiers:(rime_modifiers | release_mask)]; + } } if (changes & OSX_COMMAND_MASK) { release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask; - [self processKey:XK_Super_L - modifiers:(rime_modifiers | release_mask)]; + if (keyCode == 0x37) { + [self processKey:XK_Super_L modifiers:(rime_modifiers | release_mask)]; + } else if (keyCode == 0x36) { + [self processKey:XK_Super_R modifiers:(rime_modifiers | release_mask)]; + } // do not update UI when using Command key break; } From ee925a791b6fd3e1a4696ca01d4d3f3170cad9b3 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Dec 2022 03:13:01 +0100 Subject: [PATCH 004/164] Update localisation --- Base.lproj/MainMenu.xib | 14 +++---- Info.plist | 19 +++------- Squirrel.xcodeproj/project.pbxproj | 24 ++++++------ en.lproj/InfoPlist.strings | 4 +- en.lproj/Localizable.strings | 4 +- en.lproj/MainMenu.strings | 15 ++++++++ zh-Hans.lproj/Localizable.strings | 15 ++------ zh-Hans.lproj/MainMenu.strings | 15 ++++++++ zh-Hans.lproj/MainMenu.xib | 59 ------------------------------ zh-Hant.lproj/Localizable.strings | 23 ++++-------- zh-Hant.lproj/MainMenu.strings | 15 ++++++++ zh-Hant.lproj/MainMenu.xib | 59 ------------------------------ 12 files changed, 86 insertions(+), 180 deletions(-) create mode 100644 en.lproj/MainMenu.strings create mode 100644 zh-Hans.lproj/MainMenu.strings delete mode 100644 zh-Hans.lproj/MainMenu.xib create mode 100644 zh-Hant.lproj/MainMenu.strings delete mode 100644 zh-Hant.lproj/MainMenu.xib diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index 38859404b..8b1464468 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -1,8 +1,7 @@ - + - - + @@ -11,7 +10,7 @@ - + @@ -34,25 +33,26 @@ - + - + - + + diff --git a/Info.plist b/Info.plist index 5fb01b358..d1347237e 100644 --- a/Info.plist +++ b/Info.plist @@ -2,8 +2,6 @@ - TISInputSourceID - im.rime.inputmethod.Squirrel CFBundleDevelopmentRegion English CFBundleExecutable @@ -32,15 +30,11 @@ im.rime.inputmethod.Squirrel.Hans TISIntendedLanguage zh-Hans - tsInputModeAlternateMenuIconFileKey - rime.pdf tsInputModeCharacterRepertoireKey Hans - Han + Hant - tsInputModeDefaultStateKey - tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -60,15 +54,11 @@ im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage zh-Hant - tsInputModeAlternateMenuIconFileKey - rime.pdf tsInputModeCharacterRepertoireKey Hant - Han + Hans - tsInputModeDefaultStateKey - tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -111,11 +101,14 @@ dsa_pub.pem TICapsLockLanguageSwitchCapable + TISIconIsTemplate + + TISInputSourceID + im.rime.inputmethod.Squirrel tsInputMethodCharacterRepertoireKey Hans Hant - Han tsInputMethodIconFileKey rime.pdf diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 3a9007319..de479b0ce 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ 44AEBC7521F569FD00344375 /* key_bindings.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7221F569CF00344375 /* key_bindings.yaml */; }; 44AEBC7621F569FD00344375 /* punctuation.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7121F569CF00344375 /* punctuation.yaml */; }; 44CD640C15E2646B0021234E /* librime.1.dylib in Copy 3rd-party Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 44CD7D9E1828D981006E9222 /* rime.pdf */; }; 44E21A9016A653E700C2B08F /* rime_deployer in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8E16A653E700C2B08F /* rime_deployer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E21A9116A653E700C2B08F /* rime_dict_manager in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8F16A653E700C2B08F /* rime_dict_manager */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44E98EC214AE1AC900847AD6 /* utf8.cpp */; }; @@ -84,6 +83,7 @@ A4B8E1B30F645B870094E08B /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4B8E1B20F645B870094E08B /* Carbon.framework */; }; A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; + F49ABAEC294AB6EA00FC02B0 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F49ABAEB294AB6EA00FC02B0 /* rime.pdf */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -221,9 +221,6 @@ 44AEBC7221F569CF00344375 /* key_bindings.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = key_bindings.yaml; path = data/plum/key_bindings.yaml; sourceTree = ""; }; 44CB5E872585EFAE0022654F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 44CD640915E2633D0021234E /* librime.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librime.1.dylib; path = lib/librime.1.dylib; sourceTree = ""; }; - 44CD7D9E1828D981006E9222 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; - 44DA191A152B8CB600FB8EF0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; - 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; 44E21A8F16A653E700C2B08F /* rime_dict_manager */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_dict_manager; path = bin/rime_dict_manager; sourceTree = ""; }; 44E98EA514AE16DD00847AD6 /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; @@ -282,6 +279,9 @@ A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; A4FC48CA0F6530EF0069BE81 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; + F49ABAEB294AB6EA00FC02B0 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; + F4E91456294A249700E5A18B /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; + F4E91457294A24A100E5A18B /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -369,9 +369,9 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + F49ABAEB294AB6EA00FC02B0 /* rime.pdf */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, - 44CD7D9E1828D981006E9222 /* rime.pdf */, 44F7708E152B3334005CF491 /* dsa_pub.pem */, A4FC48C90F6530EF0069BE81 /* Localizable.strings */, 8D1107310486CEB800E47090 /* Info.plist */, @@ -553,9 +553,9 @@ 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, + F49ABAEC294AB6EA00FC02B0 /* rime.pdf in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, - 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -593,9 +593,9 @@ A45578F41146A75200592C6E /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( - 44DA191A152B8CB600FB8EF0 /* zh-Hans */, - 44DA191B152B8CBC00FB8EF0 /* zh-Hant */, 44CB5E872585EFAE0022654F /* Base */, + F4E91456294A249700E5A18B /* zh-Hans */, + F4E91457294A24A100E5A18B /* zh-Hant */, ); name = MainMenu.xib; sourceTree = ""; @@ -644,7 +644,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 13.0; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -689,7 +689,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = 13.0; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -742,7 +742,7 @@ "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)", ); HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1 = "\"$(SRCROOT)/librime/src\""; - HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/thirdparty/include\""; + HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/include\""; LD_RUNPATH_SEARCH_PATHS = ( "@loader_path/../Frameworks\n\n@loader_path/../Library/OpenSource\n\n@loader_path/../Library/Frameworks", /usr/lib, @@ -792,7 +792,7 @@ "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2)", ); HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1 = "\"$(SRCROOT)/librime/src\""; - HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/thirdparty/include\""; + HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/include\""; LD_RUNPATH_SEARCH_PATHS = ( "@loader_path/../Frameworks\n\n@loader_path/../Library/OpenSource\n\n@loader_path/../Library/Frameworks", /usr/lib, diff --git a/en.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings index 937469500..82b4ea67c 100644 --- a/en.lproj/InfoPlist.strings +++ b/en.lproj/InfoPlist.strings @@ -3,8 +3,8 @@ NSHumanReadableCopyright = "Copyleft, RIME Developers"; im.rime.inputmethod.Squirrel = "Squirrel"; -im.rime.inputmethod.Squirrel.Hans = "Squirrel - Simplified"; -im.rime.inputmethod.Squirrel.Hant = "Squirrel - Traditional"; +im.rime.inputmethod.Squirrel.Hans = "Squirrel – Simplified"; +im.rime.inputmethod.Squirrel.Hant = "Squirrel – Traditional"; CFBundleName = "Squirrel"; CFBundleDisplayName = "Squirrel"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 5e91f7017..b01d4ed9c 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -4,8 +4,8 @@ "deploy_start" = "Deploying Rime input method engine."; "deploy_success" = "Squirrel is ready."; "deploy_failure" = "Error occurred. See log file $TMPDIR/rime.squirrel.INFO."; -"ascii_mode" = "A"; -"!ascii_mode" = "中"; +"ascii_mode" = "🄰"; +"!ascii_mode" = "🈑"; "full_shape" = "Full shape"; "!full_shape" = "Half shape"; "ascii_punct" = ".,"; diff --git a/en.lproj/MainMenu.strings b/en.lproj/MainMenu.strings new file mode 100644 index 000000000..3131c1caf --- /dev/null +++ b/en.lproj/MainMenu.strings @@ -0,0 +1,15 @@ + +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "Deploy"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "Check for updates…"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "Rime Wiki…"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "Settings…"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "Sync user data"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 7f49d70d4..c74783e9f 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -1,23 +1,16 @@ -/* - Localizable.strings - Squirrel - - Created by 弓辰 on 12/12/22. - -*/ "Squirrel" = "鼠须管"; "deploy_update" = "更新输入法引擎…"; "deploy_start" = "部署输入法引擎…"; "deploy_success" = "部署完成。"; "deploy_failure" = "有错误!请查看日志 $TMPDIR/rime.squirrel.INFO"; -"ascii_mode" = "A"; -"!ascii_mode" = "中"; +"ascii_mode" = "🄰"; +"!ascii_mode" = "🈑"; "full_shape" = "全角"; "!full_shape" = "半角"; "ascii_punct" = ".,"; "!ascii_punct" = "。,"; "simplification" = "汉字"; "!simplification" = "漢字"; -"extended_charset" = "增广"; -"!extended_charset" = "通用"; +"extended_charset" = "扩展"; +"!extended_charset" = "基本"; diff --git a/zh-Hans.lproj/MainMenu.strings b/zh-Hans.lproj/MainMenu.strings new file mode 100644 index 000000000..23b07d5d7 --- /dev/null +++ b/zh-Hans.lproj/MainMenu.strings @@ -0,0 +1,15 @@ +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "重新部署"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "检查新版本…"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "在线文档…"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "用户设定…"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "同步用户数据"; + diff --git a/zh-Hans.lproj/MainMenu.xib b/zh-Hans.lproj/MainMenu.xib deleted file mode 100644 index e602c2e24..000000000 --- a/zh-Hans.lproj/MainMenu.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index 69ea17b32..d3a7a835e 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -1,23 +1,16 @@ -/* - Localizable.strings - Squirrel - - Created by 弓辰 on 12/12/22. - -*/ "Squirrel" = "鼠鬚管"; -"deploy_update" = "更新輸入法引擎…"; -"deploy_start" = "部署輸入法引擎…"; +"deploy_update" = "更新輸入法引擎⋯"; +"deploy_start" = "部署輸入法引擎⋯"; "deploy_success" = "部署完成。"; -"deploy_failure" = "有錯誤!請查看日誌 $TMPDIR/rime.squirrel.INFO"; -"ascii_mode" = "A"; -"!ascii_mode" = "中"; -"full_shape" = "全角"; -"!full_shape" = "半角"; +"deploy_failure" = "有錯誤!請查看記錄 $TMPDIR/rime.squirrel.INFO"; +"ascii_mode" = "🄰"; +"!ascii_mode" = "🈑"; +"full_shape" = "全形"; +"!full_shape" = "半形"; "ascii_punct" = ".,"; "!ascii_punct" = "。,"; "simplification" = "汉字"; "!simplification" = "漢字"; "extended_charset" = "增廣"; -"!extended_charset" = "通用"; +"!extended_charset" = "基本"; diff --git a/zh-Hant.lproj/MainMenu.strings b/zh-Hant.lproj/MainMenu.strings new file mode 100644 index 000000000..8ae834219 --- /dev/null +++ b/zh-Hant.lproj/MainMenu.strings @@ -0,0 +1,15 @@ +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "重新部署"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "檢查新版本⋯"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "線上文檔⋯"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "用戶設定⋯"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "同步用戶資料"; + diff --git a/zh-Hant.lproj/MainMenu.xib b/zh-Hant.lproj/MainMenu.xib deleted file mode 100644 index 893178260..000000000 --- a/zh-Hant.lproj/MainMenu.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 4cf2e64220ab9ae3dfea9c18542d7f41b5dd864b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Dec 2022 03:16:34 +0100 Subject: [PATCH 005/164] Modernise Object-C --- SquirrelApplicationDelegate.m | 2 +- SquirrelConfig.h | 2 +- SquirrelConfig.m | 2 +- SquirrelPanel.m | 30 +++++++++++++++--------------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 5db8e20fd..5bf849d22 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -136,7 +136,7 @@ - (void)shutdownRime { -(void)loadSettings { _config = [[SquirrelConfig alloc] init]; - if (![_config openBaseConfig]) { + if (!_config.openBaseConfig) { return; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index c4bd50145..b91a384d8 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -9,7 +9,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; @property(nonatomic, copy) NSString *colorSpace; @property(nonatomic, readonly) NSString *schemaId; -- (BOOL)openBaseConfig; +@property (NS_NONATOMIC_IOSONLY, readonly) BOOL openBaseConfig; - (BOOL)openWithSchemaId:(NSString *)schemaId baseConfig:(SquirrelConfig *)config; - (void)close; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index d816f1b40..698f350e7 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -155,7 +155,7 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { #pragma mark - Private methods - (id)cachedValueOfClass:(Class)aClass forKey:(NSString *)key { - id value = [_cache objectForKey:key]; + id value = _cache[key]; if (value && [value isKindOfClass:aClass]) { return value; } diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 414ab6e31..fa865b9b3 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -178,7 +178,7 @@ @interface SquirrelView : NSView @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, assign) CGFloat seperatorWidth; -- (BOOL)isFlipped; +@property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; - (void)setText:(NSAttributedString *)text; - (void)drawViewWith:(NSRange)hilightedRange preeditRange:(NSRange)preeditRange @@ -278,8 +278,8 @@ - (void)drawViewWith:(NSRange)hilightedRange NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) return path; - NSPoint previousPoint = [vertex[vertex.count-1] pointValue]; - NSPoint point = [vertex[0] pointValue]; + NSPoint previousPoint = (vertex[vertex.count-1]).pointValue; + NSPoint point = (vertex[0]).pointValue; NSPoint nextPoint; NSPoint control1; NSPoint control2; @@ -292,9 +292,9 @@ - (void)drawViewWith:(NSRange)hilightedRange } [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; i += 1) { - previousPoint = [vertex[(vertex.count+i-1)%vertex.count] pointValue]; - point = [vertex[i] pointValue]; - nextPoint = [vertex[(i+1)%vertex.count] pointValue]; + previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; + point = (vertex[i]).pointValue; + nextPoint = (vertex[(i+1)%vertex.count]).pointValue; target = point; control1 = point; diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); @@ -333,10 +333,10 @@ - (void)drawViewWith:(NSRange)hilightedRange void xyTranslation(NSMutableArray *shape, NSPoint direction) { for (NSUInteger i = 0; i < shape.count; i += 1) { - NSPoint point = [shape[i] pointValue]; + NSPoint point = (shape[i]).pointValue; point.x += direction.x; point.y += direction.y; - [shape replaceObjectAtIndex:i withObject:@(point)]; + shape[i] = @(point); } } @@ -440,7 +440,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe // If the point is outside the innerBox, will extend to reach the outerBox void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerBorder) { for (NSUInteger i = 0; i < vertex.count; i += 1){ - NSPoint point = [vertex[i] pointValue]; + NSPoint point = (vertex[i]).pointValue; if (point.x < innerBorder.origin.x) { point.x = outerBorder.origin.x; } else if (point.x > innerBorder.origin.x+innerBorder.size.width) { @@ -451,7 +451,7 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } else if (point.y > innerBorder.origin.y+innerBorder.size.height) { point.y = outerBorder.origin.y+outerBorder.size.height; } - [vertex replaceObjectAtIndex:i withObject:@(point)]; + vertex[i] = @(point); } } @@ -662,21 +662,21 @@ - (void)drawRect:(NSRect)dirtyRect { [theme.backgroundColor setFill]; [backgroundPath fill]; - if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { + if (theme.preeditBackgroundColor && !preeditPath.empty) { [theme.preeditBackgroundColor setFill]; [preeditPath fill]; } - if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { + if (theme.highlightedStripColor && !highlightedPath.empty) { [theme.highlightedStripColor setFill]; [highlightedPath fill]; - if (![highlightedPath2 isEmpty]) { + if (!highlightedPath2.empty) { [highlightedPath2 fill]; } } - if (theme.highlightedPreeditColor && ![highlightedPreeditPath isEmpty]) { + if (theme.highlightedPreeditColor && !highlightedPreeditPath.empty) { [theme.highlightedPreeditColor setFill]; [highlightedPreeditPath fill]; - if (![highlightedPreeditPath2 isEmpty]) { + if (!highlightedPreeditPath2.empty) { [highlightedPreeditPath2 fill]; } } From efe566523a474f42d9da169677d566f017b8ad66 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Dec 2022 03:12:02 +0100 Subject: [PATCH 006/164] Customise default keyboard layout --- SquirrelInputController.m | 12 ++++++++++-- data/squirrel.yaml | 6 +++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 05873a8a2..2ed3a5e62 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -285,8 +285,16 @@ -(NSUInteger)recognizedEvents:(id)sender -(void)activateServer:(id)sender { //NSLog(@"activateServer:"); - if ([NSApp.squirrelAppDelegate.config getBool:@"us_keyboard_layout"]) { - [sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"]; + NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; + if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { + keyboardLayout = NULL; + } else if ([keyboardLayout isEqualToString:@"default"]) { + keyboardLayout = @"com.apple.keylayout.ABC"; + } else if (![keyboardLayout hasPrefix:@"com.apple.keylayout."]) { + keyboardLayout = [NSString stringWithFormat:@"com.apple.keylayout.%@", keyboardLayout]; + } + if (keyboardLayout) { + [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } _preeditString = @""; } diff --git a/data/squirrel.yaml b/data/squirrel.yaml index ca162b2a9..af37b6775 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -3,7 +3,11 @@ config_version: '0.37' -us_keyboard_layout: false +# options: last | default | _custom_ +# last: the last used latin keyboard layout +# default: US (ABC) keyboard layout +# _custom_: keyboard layout of your choice, e.g. 'com.apple.keylayout.USExtended' +keyboard_layout: default # for veteran chord-typist chord_duration: 0.1 # seconds From 188576425d04b6a9f3f16ac4eaa3224796f4aa0b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Dec 2022 03:15:25 +0100 Subject: [PATCH 007/164] Bump up librime version --- .gitmodules | 4 +++- INSTALL.md | 6 +++--- Makefile | 16 ++++++++-------- librime | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.gitmodules b/.gitmodules index 253b9170d..0ac4d6b46 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,9 @@ [submodule "librime"] path = librime - url = https://github.com/rime/librime.git + url = https://github.com/groverlynn/librime.git ignore = dirty + branch = test [submodule "plum"] path = plum url = https://github.com/rime/plum.git + ignore = dirty diff --git a/INSTALL.md b/INSTALL.md index 0d45c82c5..aab8b81ab 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample # ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram # ... ``` ### Shortcut: get the latest librime release @@ -59,9 +59,9 @@ Choose one of the following options. ``` sh export BUILD_UNIVERSAL=1 -make -C librime xcode/thirdparty/boost +make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/thirdparty/src/boost_1_75_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/Makefile b/Makefile index f0427f0a5..dffc7421e 100644 --- a/Makefile +++ b/Makefile @@ -12,11 +12,11 @@ RIME_LIB_DIR = librime/dist/lib RIME_LIBRARY_FILE_NAME = librime.1.dylib RIME_LIBRARY = lib/$(RIME_LIBRARY_FILE_NAME) -RIME_DEPS = librime/thirdparty/lib/libcapnp.a \ - librime/thirdparty/lib/libmarisa.a \ - librime/thirdparty/lib/libleveldb.a \ - librime/thirdparty/lib/libopencc.a \ - librime/thirdparty/lib/libyaml-cpp.a +RIME_DEPS = librime/lib/libcapnp.a \ + librime/lib/libmarisa.a \ + librime/lib/libleveldb.a \ + librime/lib/libopencc.a \ + librime/lib/libyaml-cpp.a PLUM_DATA = bin/rime-install \ data/plum/default.yaml \ data/plum/symbols.yaml \ @@ -26,7 +26,7 @@ OPENCC_DATA = data/opencc/TSCharacters.ocd2 \ data/opencc/t2s.json DEPS_CHECK = $(RIME_LIBRARY) $(PLUM_DATA) $(OPENCC_DATA) -OPENCC_DATA_OUTPUT = librime/thirdparty/share/opencc/*.* +OPENCC_DATA_OUTPUT = librime/share/opencc/*.* PLUM_DATA_OUTPUT = plum/output/*.* RIME_PACKAGE_INSTALLER = plum/rime-install @@ -39,7 +39,7 @@ $(RIME_LIBRARY): $(MAKE) librime $(RIME_DEPS): - $(MAKE) -C librime xcode/thirdparty + $(MAKE) -C librime xcode/deps librime: $(RIME_DEPS) $(MAKE) -C librime $(RIME_DIST_TARGET) @@ -67,7 +67,7 @@ plum-data: $(MAKE) copy-plum-data opencc-data: - $(MAKE) -C librime xcode/thirdparty/opencc + $(MAKE) -C librime xcode/deps/opencc $(MAKE) copy-opencc-data copy-plum-data: diff --git a/librime b/librime index 99e269c8e..cda7df0bf 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 99e269c8eb251deddbad9b0d2c4d965b228f8006 +Subproject commit cda7df0bfd235c1a4ed5eb4120c6ed2570ca7ffd From 384ef1c30456067707ffe1343803c434bc3a9e97 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 18 Jan 2023 08:26:26 +0100 Subject: [PATCH 008/164] Customise default keyboard layout --- SquirrelInputController.m | 12 ++++++++++-- data/squirrel.yaml | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index eec29ffac..004ba8a19 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -270,8 +270,16 @@ -(NSUInteger)recognizedEvents:(id)sender -(void)activateServer:(id)sender { //NSLog(@"activateServer:"); - if ([NSApp.squirrelAppDelegate.config getBool:@"us_keyboard_layout"]) { - [sender overrideKeyboardWithKeyboardNamed:@"com.apple.keylayout.US"]; + NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; + if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { + keyboardLayout = NULL; + } else if ([keyboardLayout isEqualToString:@"default"]) { + keyboardLayout = @"com.apple.keylayout.ABC"; + } else if (![keyboardLayout hasPrefix:@"com.apple.keylayout."]) { + keyboardLayout = [NSString stringWithFormat:@"com.apple.keylayout.%@", keyboardLayout]; + } + if (keyboardLayout) { + [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } _preeditString = @""; } diff --git a/data/squirrel.yaml b/data/squirrel.yaml index af37b6775..7f0f1ecb2 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -3,10 +3,10 @@ config_version: '0.37' -# options: last | default | _custom_ +# options: last | default | # last: the last used latin keyboard layout # default: US (ABC) keyboard layout -# _custom_: keyboard layout of your choice, e.g. 'com.apple.keylayout.USExtended' +# : keyboard layout of your choice, e.g. 'com.apple.keylayout.USExtended' or 'USExtended' keyboard_layout: default # for veteran chord-typist From db59b21083e7afc0a405dfbd308b7a461bea2e3f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 14 Jan 2023 23:49:12 +0100 Subject: [PATCH 009/164] Update librime --- .gitmodules | 2 -- INSTALL.md | 9 +++++++++ Squirrel.xcodeproj/project.pbxproj | 4 ++-- librime | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 0ac4d6b46..3f552890a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,7 @@ [submodule "librime"] path = librime url = https://github.com/groverlynn/librime.git - ignore = dirty branch = test [submodule "plum"] path = plum url = https://github.com/rime/plum.git - ignore = dirty diff --git a/INSTALL.md b/INSTALL.md index aab8b81ab..ac6999e20 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -129,8 +129,17 @@ Once built, you can install and try it live on your Mac computer: # Squirrel as a Universal app make install +# for Mac computers with Apple Silicon +make ARCHS='arm64' install + # for Intel-based Mac only make ARCHS='x86_64' install ``` +To clean-up the building files: + +``` sh +make clean clean-deps +``` + That's it, a verbal journal. Thanks for riming with Squirrel. diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index de479b0ce..0e5cac9de 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -749,7 +749,7 @@ /usr/local/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -798,7 +798,7 @@ /usr/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; diff --git a/librime b/librime index cda7df0bf..233bc6b14 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit cda7df0bfd235c1a4ed5eb4120c6ed2570ca7ffd +Subproject commit 233bc6b1489d97fd8a8ea64842739cc67f0c29df From a0bc54841e7cfa576794ea1d343f5a734adcbc9b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 24 Jan 2023 05:29:49 +0100 Subject: [PATCH 010/164] Update plum --- .gitmodules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 3f552890a..b996b66d2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,4 +4,5 @@ branch = test [submodule "plum"] path = plum - url = https://github.com/rime/plum.git + url = https://github.com/groverlynn/plum.git + branch = test From 6e0c8951aef91ab40b7d1b138654bc4b1016f55c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 24 Jan 2023 01:39:39 +0100 Subject: [PATCH 011/164] Register squirrel into Cantonese --- Info.plist | 47 +++++++++++++++++++++--- Squirrel.xcodeproj/project.pbxproj | 27 +++++++++----- en.lproj/InfoPlist.strings | 3 +- en.lproj/Localizable.strings | 14 +------ input_source.m | 26 +++++++++---- librime | 2 +- zh-HK.lproj/InfoPlist.strings | 11 ++++++ zh-HK.lproj/Localizable.strings | 6 +++ zh-HK.lproj/MainMenu.xib | 59 ++++++++++++++++++++++++++++++ zh-Hans.lproj/InfoPlist.strings | 3 +- zh-Hans.lproj/Localizable.strings | 10 ----- zh-Hans.lproj/MainMenu.strings | 15 -------- zh-Hans.lproj/MainMenu.xib | 59 ++++++++++++++++++++++++++++++ zh-Hant.lproj/InfoPlist.strings | 3 +- zh-Hant.lproj/Localizable.strings | 12 +----- zh-Hant.lproj/MainMenu.strings | 15 -------- zh-Hant.lproj/MainMenu.xib | 59 ++++++++++++++++++++++++++++++ 17 files changed, 280 insertions(+), 91 deletions(-) create mode 100644 zh-HK.lproj/InfoPlist.strings create mode 100644 zh-HK.lproj/Localizable.strings create mode 100644 zh-HK.lproj/MainMenu.xib delete mode 100644 zh-Hans.lproj/MainMenu.strings create mode 100644 zh-Hans.lproj/MainMenu.xib delete mode 100644 zh-Hant.lproj/MainMenu.strings create mode 100644 zh-Hant.lproj/MainMenu.xib diff --git a/Info.plist b/Info.plist index d1347237e..83473bdac 100644 --- a/Info.plist +++ b/Info.plist @@ -2,6 +2,8 @@ + TISInputSourceID + im.rime.inputmethod.Squirrel CFBundleDevelopmentRegion English CFBundleExecutable @@ -24,17 +26,49 @@ tsInputModeListKey + im.rime.inputmethod.Squirrel.Cant + + TISInputSourceID + im.rime.inputmethod.Squirrel.Cant + TISIntendedLanguage + yue-Hant + tsInputModeAlternateMenuIconFileKey + rime.pdf + tsInputModeCharacterRepertoireKey + + Hant + Hans + + tsInputModeDefaultStateKey + + tsInputModeIsVisibleKey + + tsInputModeKeyEquivalentModifiersKey + 4608 + tsInputModeMenuIconFileKey + rime.pdf + tsInputModePaletteIconFileKey + rime.pdf + tsInputModePrimaryInScriptKey + + tsInputModeScriptKey + smUnicodeScript + im.rime.inputmethod.Squirrel.Hans TISInputSourceID im.rime.inputmethod.Squirrel.Hans TISIntendedLanguage zh-Hans + tsInputModeAlternateMenuIconFileKey + rime.pdf tsInputModeCharacterRepertoireKey Hans Hant + tsInputModeDefaultStateKey + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -54,11 +88,15 @@ im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage zh-Hant + tsInputModeAlternateMenuIconFileKey + rime.pdf tsInputModeCharacterRepertoireKey Hant Hans + tsInputModeDefaultStateKey + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -75,8 +113,9 @@ tsVisibleInputModeOrderedArrayKey - im.rime.inputmethod.Squirrel.Hans im.rime.inputmethod.Squirrel.Hant + im.rime.inputmethod.Squirrel.Hans + im.rime.inputmethod.Squirrel.Cant InputMethodConnectionName @@ -101,14 +140,10 @@ dsa_pub.pem TICapsLockLanguageSwitchCapable - TISIconIsTemplate - - TISInputSourceID - im.rime.inputmethod.Squirrel tsInputMethodCharacterRepertoireKey - Hans Hant + Hans tsInputMethodIconFileKey rime.pdf diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 0e5cac9de..7b798ec64 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 44AEBC7521F569FD00344375 /* key_bindings.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7221F569CF00344375 /* key_bindings.yaml */; }; 44AEBC7621F569FD00344375 /* punctuation.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7121F569CF00344375 /* punctuation.yaml */; }; 44CD640C15E2646B0021234E /* librime.1.dylib in Copy 3rd-party Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 44CD7D9E1828D981006E9222 /* rime.pdf */; }; 44E21A9016A653E700C2B08F /* rime_deployer in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8E16A653E700C2B08F /* rime_deployer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E21A9116A653E700C2B08F /* rime_dict_manager in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8F16A653E700C2B08F /* rime_dict_manager */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44E98EC214AE1AC900847AD6 /* utf8.cpp */; }; @@ -83,7 +84,6 @@ A4B8E1B30F645B870094E08B /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4B8E1B20F645B870094E08B /* Carbon.framework */; }; A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; - F49ABAEC294AB6EA00FC02B0 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F49ABAEB294AB6EA00FC02B0 /* rime.pdf */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -221,6 +221,9 @@ 44AEBC7221F569CF00344375 /* key_bindings.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = key_bindings.yaml; path = data/plum/key_bindings.yaml; sourceTree = ""; }; 44CB5E872585EFAE0022654F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 44CD640915E2633D0021234E /* librime.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librime.1.dylib; path = lib/librime.1.dylib; sourceTree = ""; }; + 44CD7D9E1828D981006E9222 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; + 44DA191A152B8CB600FB8EF0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; + 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; 44E21A8F16A653E700C2B08F /* rime_dict_manager */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_dict_manager; path = bin/rime_dict_manager; sourceTree = ""; }; 44E98EA514AE16DD00847AD6 /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; @@ -279,9 +282,9 @@ A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; A4FC48CA0F6530EF0069BE81 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; - F49ABAEB294AB6EA00FC02B0 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; - F4E91456294A249700E5A18B /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; - F4E91457294A24A100E5A18B /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; + F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; + F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; + F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -369,9 +372,9 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( - F49ABAEB294AB6EA00FC02B0 /* rime.pdf */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, + 44CD7D9E1828D981006E9222 /* rime.pdf */, 44F7708E152B3334005CF491 /* dsa_pub.pem */, A4FC48C90F6530EF0069BE81 /* Localizable.strings */, 8D1107310486CEB800E47090 /* Info.plist */, @@ -530,6 +533,7 @@ en, "zh-Hans", "zh-Hant", + "zh-HK", Base, ); mainGroup = 29B97314FDCFA39411CA2CEA /* Squirrel */; @@ -553,9 +557,9 @@ 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, - F49ABAEC294AB6EA00FC02B0 /* rime.pdf in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, + 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -586,6 +590,7 @@ 089C165DFE840E0CC02AAC07 /* en */, 446D18E014F0191200EC3116 /* zh-Hans */, 446D18E114F0193100EC3116 /* zh-Hant */, + F4A90995297F63AE00D9F520 /* zh-HK */, ); name = InfoPlist.strings; sourceTree = ""; @@ -593,9 +598,10 @@ A45578F41146A75200592C6E /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( + 44DA191A152B8CB600FB8EF0 /* zh-Hans */, + 44DA191B152B8CBC00FB8EF0 /* zh-Hant */, + F4A90996297F63AE00D9F520 /* zh-HK */, 44CB5E872585EFAE0022654F /* Base */, - F4E91456294A249700E5A18B /* zh-Hans */, - F4E91457294A24A100E5A18B /* zh-Hant */, ); name = MainMenu.xib; sourceTree = ""; @@ -606,6 +612,7 @@ A4FC48CA0F6530EF0069BE81 /* en */, 44FA4D891685997300116C1F /* zh-Hans */, 44FA4D8E16859B2900116C1F /* zh-Hant */, + F4A90994297F63AE00D9F520 /* zh-HK */, ); name = Localizable.strings; sourceTree = ""; @@ -644,7 +651,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -689,7 +696,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 13.0; + MACOSX_DEPLOYMENT_TARGET = 10.9; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", diff --git a/en.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings index 82b4ea67c..3d41853d7 100644 --- a/en.lproj/InfoPlist.strings +++ b/en.lproj/InfoPlist.strings @@ -1,10 +1,11 @@ /* Localized versions of Info.plist keys */ -NSHumanReadableCopyright = "Copyleft, RIME Developers"; +NSHumanReadableCopyright = "Copyleft 🄯 RIME Developers"; im.rime.inputmethod.Squirrel = "Squirrel"; im.rime.inputmethod.Squirrel.Hans = "Squirrel – Simplified"; im.rime.inputmethod.Squirrel.Hant = "Squirrel – Traditional"; +im.rime.inputmethod.Squirrel.Cant = "Squirrel – Cantonese"; CFBundleName = "Squirrel"; CFBundleDisplayName = "Squirrel"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index b01d4ed9c..02184186b 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -1,16 +1,6 @@ "Squirrel" = "Squirrel"; -"deploy_update" = "Deploying Rime for updates."; -"deploy_start" = "Deploying Rime input method engine."; +"deploy_update" = "Deploying Rime for updates…"; +"deploy_start" = "Deploying Rime input method engine…"; "deploy_success" = "Squirrel is ready."; "deploy_failure" = "Error occurred. See log file $TMPDIR/rime.squirrel.INFO."; -"ascii_mode" = "🄰"; -"!ascii_mode" = "🈑"; -"full_shape" = "Full shape"; -"!full_shape" = "Half shape"; -"ascii_punct" = ".,"; -"!ascii_punct" = "。,"; -"simplification" = "Simplified"; -"!simplification" = "Traditional"; -"extended_charset" = "CJK extended"; -"!extended_charset" = "CJK baseset"; diff --git a/input_source.m b/input_source.m index be983a142..85b7e9739 100644 --- a/input_source.m +++ b/input_source.m @@ -4,8 +4,12 @@ "/Library/Input Methods/Squirrel.app"; static NSString *const kSourceID = @"im.rime.inputmethod.Squirrel"; -static NSString *const kInputModeID = +static NSString *const kInputModeHantID = + @"im.rime.inputmethod.Squirrel.Hant"; +static NSString *const kInputModeHansID = @"im.rime.inputmethod.Squirrel.Hans"; +static NSString *const kInputModeCantID = + @"im.rime.inputmethod.Squirrel.Cant"; void RegisterInputSource() { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( @@ -25,8 +29,10 @@ void ActivateInputSource() { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"examining input source '%@", sourceID); - if ([sourceID isEqualToString:kSourceID] || - [sourceID isEqualToString:kInputModeID]) { + if ([sourceID isEqualToString:kInputModeHantID] || + [sourceID isEqualToString:kInputModeHansID] || + [sourceID isEqualToString:kInputModeCantID] || + [sourceID isEqualToString:kSourceID]) { TISEnableInputSource(inputSource); NSLog(@"Enabled input source: %@", sourceID); CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( @@ -48,8 +54,10 @@ void DeactivateInputSource() { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kSourceID] || - [sourceID isEqualToString:kInputModeID]) { + if ([sourceID isEqualToString:kInputModeHantID] || + [sourceID isEqualToString:kInputModeHansID] || + [sourceID isEqualToString:kInputModeCantID] || + [sourceID isEqualToString:kSourceID]) { TISDisableInputSource(inputSource); NSLog(@"Disabled input source: %@", sourceID); } @@ -66,8 +74,10 @@ BOOL IsInputSourceActive() { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kSourceID] || - [sourceID isEqualToString:kInputModeID]) { + if ([sourceID isEqualToString:kInputModeHantID] || + [sourceID isEqualToString:kInputModeHansID] || + [sourceID isEqualToString:kInputModeCantID] || + [sourceID isEqualToString:kSourceID]) { CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceIsEnabled)); if (CFBooleanGetValue(isEnabled)) { @@ -77,5 +87,5 @@ BOOL IsInputSourceActive() { } CFRelease(sourceList); //NSLog(@"IsInputSourceActive: %d", active); - return active == 2; // 1 active input method + 1 active input mode + return active >= 2; // 1 active input method + 1 active input mode } diff --git a/librime b/librime index 233bc6b14..0d8b1e6ef 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 233bc6b1489d97fd8a8ea64842739cc67f0c29df +Subproject commit 0d8b1e6ef28a04977da03dfff4c4e77ba4e21ce4 diff --git a/zh-HK.lproj/InfoPlist.strings b/zh-HK.lproj/InfoPlist.strings new file mode 100644 index 000000000..4e8f3bef3 --- /dev/null +++ b/zh-HK.lproj/InfoPlist.strings @@ -0,0 +1,11 @@ +/* Localized versions of Info.plist keys */ + +NSHumanReadableCopyright = "式恕堂🄯版權所無"; + +im.rime.inputmethod.Squirrel = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Hans = "鼠须管"; +im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; + +CFBundleName = "鼠鬚管"; +CFBundleDisplayName = "鼠鬚管"; diff --git a/zh-HK.lproj/Localizable.strings b/zh-HK.lproj/Localizable.strings new file mode 100644 index 000000000..47f3756d1 --- /dev/null +++ b/zh-HK.lproj/Localizable.strings @@ -0,0 +1,6 @@ +"Squirrel" = "鼠鬚筆"; + +"deploy_update" = "更新輸入法引擎⋯"; +"deploy_start" = "部署輸入法引擎⋯"; +"deploy_success" = "部署完成。"; +"deploy_failure" = "有錯誤!請查看日誌 $TMPDIR/rime.squirrel.INFO"; diff --git a/zh-HK.lproj/MainMenu.xib b/zh-HK.lproj/MainMenu.xib new file mode 100644 index 000000000..722c6e1b2 --- /dev/null +++ b/zh-HK.lproj/MainMenu.xib @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh-Hans.lproj/InfoPlist.strings b/zh-Hans.lproj/InfoPlist.strings index 250343f91..4a06de222 100644 --- a/zh-Hans.lproj/InfoPlist.strings +++ b/zh-Hans.lproj/InfoPlist.strings @@ -1,10 +1,11 @@ /* Localized versions of Info.plist keys */ -NSHumanReadableCopyright = "式恕堂 版权所无"; +NSHumanReadableCopyright = "式恕堂🄯版权所无"; im.rime.inputmethod.Squirrel = "鼠须管"; im.rime.inputmethod.Squirrel.Hans = "鼠须管"; im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; CFBundleName = "鼠须管"; CFBundleDisplayName = "鼠须管"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index c74783e9f..1e49ceda3 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -4,13 +4,3 @@ "deploy_start" = "部署输入法引擎…"; "deploy_success" = "部署完成。"; "deploy_failure" = "有错误!请查看日志 $TMPDIR/rime.squirrel.INFO"; -"ascii_mode" = "🄰"; -"!ascii_mode" = "🈑"; -"full_shape" = "全角"; -"!full_shape" = "半角"; -"ascii_punct" = ".,"; -"!ascii_punct" = "。,"; -"simplification" = "汉字"; -"!simplification" = "漢字"; -"extended_charset" = "扩展"; -"!extended_charset" = "基本"; diff --git a/zh-Hans.lproj/MainMenu.strings b/zh-Hans.lproj/MainMenu.strings deleted file mode 100644 index 23b07d5d7..000000000 --- a/zh-Hans.lproj/MainMenu.strings +++ /dev/null @@ -1,15 +0,0 @@ -/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ -"774.title" = "重新部署"; - -/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ -"776.title" = "检查新版本…"; - -/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ -"797.title" = "在线文档…"; - -/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ -"802.title" = "用户设定…"; - -/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ -"804.title" = "同步用户数据"; - diff --git a/zh-Hans.lproj/MainMenu.xib b/zh-Hans.lproj/MainMenu.xib new file mode 100644 index 000000000..d7ccc616c --- /dev/null +++ b/zh-Hans.lproj/MainMenu.xib @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zh-Hant.lproj/InfoPlist.strings b/zh-Hant.lproj/InfoPlist.strings index 30cb1d9a9..4e8f3bef3 100644 --- a/zh-Hant.lproj/InfoPlist.strings +++ b/zh-Hant.lproj/InfoPlist.strings @@ -1,10 +1,11 @@ /* Localized versions of Info.plist keys */ -NSHumanReadableCopyright = "式恕堂 版權所無"; +NSHumanReadableCopyright = "式恕堂🄯版權所無"; im.rime.inputmethod.Squirrel = "鼠鬚管"; im.rime.inputmethod.Squirrel.Hans = "鼠须管"; im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; CFBundleName = "鼠鬚管"; CFBundleDisplayName = "鼠鬚管"; diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index d3a7a835e..3880098db 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -3,14 +3,4 @@ "deploy_update" = "更新輸入法引擎⋯"; "deploy_start" = "部署輸入法引擎⋯"; "deploy_success" = "部署完成。"; -"deploy_failure" = "有錯誤!請查看記錄 $TMPDIR/rime.squirrel.INFO"; -"ascii_mode" = "🄰"; -"!ascii_mode" = "🈑"; -"full_shape" = "全形"; -"!full_shape" = "半形"; -"ascii_punct" = ".,"; -"!ascii_punct" = "。,"; -"simplification" = "汉字"; -"!simplification" = "漢字"; -"extended_charset" = "增廣"; -"!extended_charset" = "基本"; +"deploy_failure" = "有錯誤!請查看日誌 $TMPDIR/rime.squirrel.INFO"; diff --git a/zh-Hant.lproj/MainMenu.strings b/zh-Hant.lproj/MainMenu.strings deleted file mode 100644 index 8ae834219..000000000 --- a/zh-Hant.lproj/MainMenu.strings +++ /dev/null @@ -1,15 +0,0 @@ -/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ -"774.title" = "重新部署"; - -/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ -"776.title" = "檢查新版本⋯"; - -/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ -"797.title" = "線上文檔⋯"; - -/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ -"802.title" = "用戶設定⋯"; - -/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ -"804.title" = "同步用戶資料"; - diff --git a/zh-Hant.lproj/MainMenu.xib b/zh-Hant.lproj/MainMenu.xib new file mode 100644 index 000000000..829e72be4 --- /dev/null +++ b/zh-Hant.lproj/MainMenu.xib @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e76456ad142b4bcc672523054c1816ef9f395973 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 30 Jan 2023 10:58:40 +0100 Subject: [PATCH 012/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 0d8b1e6ef..a004f59b0 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 0d8b1e6ef28a04977da03dfff4c4e77ba4e21ce4 +Subproject commit a004f59b099cdc2ea0624c3ec2cc1a01f43e3cff From 86bf3aa375a1ca4d292c06869a953db4027cfd15 Mon Sep 17 00:00:00 2001 From: LEO Yoon-Tsaw Date: Sun, 29 Jan 2023 10:24:47 -0500 Subject: [PATCH 013/164] Adopt NSTextView along with new UI features * Float font size * No line-break within short candidate and comment * Highlighted candidate background shadow * Non-highlighted candidate background color * Optional mutual exclusive color filling * Option to disable sticky panel size --- SquirrelPanel.m | 1047 ++++++++++++++++++++++++++--------------------- 1 file changed, 588 insertions(+), 459 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 4f8d829e1..e93734587 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,76 +3,35 @@ #import "SquirrelConfig.h" #import -@implementation NSBezierPath (BezierPathQuartzUtilities) -// This method works only in OS X v10.2 and later. -- (CGPathRef)quartzPath { - NSInteger i, numElements; - // Need to begin a path here. - CGPathRef immutablePath = NULL; - - // Then draw the path elements. - numElements = [self elementCount]; - if (numElements > 0) { - CGMutablePathRef path = CGPathCreateMutable(); - NSPoint points[3]; - BOOL didClosePath = YES; - for (i = 0; i < numElements; i++) { - switch ([self elementAtIndex:i associatedPoints:points]) { - case NSMoveToBezierPathElement: - CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); - break; - case NSLineToBezierPathElement: - CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); - didClosePath = NO; - break; - case NSCurveToBezierPathElement: - CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, - points[1].x, points[1].y, - points[2].x, points[2].y); - didClosePath = NO; - break; - case NSClosePathBezierPathElement: - CGPathCloseSubpath(path); - didClosePath = YES; - break; - } - } - - // Be sure the path is closed or Quartz may not do valid hit detection. - if (!didClosePath) { - CGPathCloseSubpath(path); - } - immutablePath = CGPathCreateCopy(path); - CGPathRelease(path); - } - return immutablePath; -} -@end - static const CGFloat kOffsetHeight = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kDefaultCandidateFormat = @"%c.\u00A0%@"; @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL native; +@property(nonatomic, assign) BOOL memorizeSize; @property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSColor *highlightedStripColor; +@property(nonatomic, strong, readonly) NSColor *highlightedBackColor; +@property(nonatomic, strong, readonly) NSColor *candidateBackColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @property(nonatomic, strong, readonly) NSColor *borderColor; @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; +@property(nonatomic, readonly) CGFloat surroundingExtraExpansion; +@property(nonatomic, readonly) CGFloat shadowSize; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; +@property(nonatomic, readonly) BOOL mutualExclusive; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -94,32 +53,36 @@ @interface SquirrelTheme : NSObject - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedStripColor:(NSColor *)highlightedStripColor + highlightedBackColor:(NSColor *)highlightedBackColor + candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius + srdExtraExpansion:(CGFloat)surroundingExtraExpansion + shadowSize:(CGFloat)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(BOOL)translucency + mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate; -- (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; @@ -155,12 +118,14 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedStripColor:(NSColor *)highlightedStripColor + highlightedBackColor:(NSColor *)highlightedBackColor + candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; - _highlightedStripColor = highlightedStripColor; + _highlightedBackColor = highlightedBackColor; + _candidateBackColor = candidateBackColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; _borderColor = borderColor; @@ -168,23 +133,29 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(double)cornerRadius hilitedCornerRadius:(double)hilitedCornerRadius + srdExtraExpansion:(double)surroundingExtraExpansion + shadowSize:(double)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(CGFloat)alpha + alpha:(double)alpha translucency:(BOOL)translucency + mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; + _surroundingExtraExpansion = surroundingExtraExpansion; + _shadowSize = shadowSize; _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; _alpha = alpha; _translucency = translucency; + _mutualExclusive = mutualExclusive; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -192,17 +163,17 @@ - (void)setCornerRadius:(double)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { _attrs = attrs; - _labelAttrs = labelAttrs; _highlightedAttrs = highlightedAttrs; + _labelAttrs = labelAttrs; _labelHighlightedAttrs = labelHighlightedAttrs; _commentAttrs = commentAttrs; _commentHighlightedAttrs = commentHighlightedAttrs; @@ -220,8 +191,9 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextStorage *text; -@property(nonatomic, readonly) NSRange highlightedRange; +@property(nonatomic, readonly) NSTextView *textView; +@property(nonatomic, readonly) NSArray *candidateRanges; +@property(nonatomic, readonly) NSInteger hilightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRect contentRect; @@ -230,11 +202,10 @@ @interface SquirrelView : NSView @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; -@property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; -- (void)setText:(NSAttributedString *)text; -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; +- (void) drawViewWith:(NSArray *)candidateRanges + hilightedIndex:(NSInteger)hilightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -271,45 +242,60 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } - // Use textStorage to store text and manage all text layout and draws + _textView = [[NSTextView alloc] initWithFrame:frameRect]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - _text = [[NSTextStorage alloc] init]; - [_text addLayoutManager:layoutManager]; - layoutManager.backgroundLayoutEnabled = YES; + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + [_textView replaceTextContainer:textContainer]; + _textView.layoutManager.backgroundLayoutEnabled = YES; _defaultTheme = [[SquirrelTheme alloc] init]; - _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } + _shape = [[CAShapeLayer alloc] init]; return self; } // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; + __block long actualWidth = 0; + [_textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { + NSRange range = [self.textView.layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + NSAttributedString *str = [self.textView.textStorage attributedSubstringFromRange:range]; + NSRange nonWhiteRange = [str.string rangeOfCharacterFromSet:NSCharacterSet.whitespaceAndNewlineCharacterSet.invertedSet options:NSBackwardsSearch]; + if (nonWhiteRange.location != NSNotFound) { + NSRange newRange = NSMakeRange(range.location, NSMaxRange(nonWhiteRange)); + NSRange newGlyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:newRange actualCharacterRange:NULL]; + CGFloat width = [self.textView.layoutManager boundingRectForGlyphRange:newGlyphRange inTextContainer:self.textView.textContainer].size.width; + if (width > actualWidth) { + actualWidth = width; + } + } + }]; + if (actualWidth > 0) { + rect.size.width = actualWidth; + } return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } -- (void)setText:(NSAttributedString *)text { - [_text setAttributedString:[text copy]]; -} - // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { - _highlightedRange = hilightedRange; +- (void) drawViewWith:(NSArray *)candidateRanges + hilightedIndex:(NSInteger)hilightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange { + _candidateRanges = candidateRanges; + _hilightedIndex = hilightedIndex; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; self.needsDisplay = YES; @@ -321,57 +307,57 @@ - (void)drawViewWith:(NSRange)hilightedRange return 1; } else if (number <= -2) { return -1; - }else { + } else { return number / 2; } } // Bezier cubic curve, which has continuous roundness -NSBezierPath *drawSmoothLines(NSArray *vertex, CGFloat alpha, CGFloat beta) { - NSBezierPath *path = [NSBezierPath bezierPath]; +CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * __nullable straightCorner, CGFloat alpha, CGFloat beta) { + beta = MAX(0.00001, beta); + CGMutablePathRef path = CGPathCreateMutable(); if (vertex.count < 1) return path; - NSPoint previousPoint = (vertex[vertex.count-1]).pointValue; - NSPoint point = (vertex[0]).pointValue; + NSPoint previousPoint = [vertex[vertex.count-1] pointValue]; + NSPoint point = [vertex[0] pointValue]; NSPoint nextPoint; NSPoint control1; NSPoint control2; NSPoint target = previousPoint; NSPoint diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { + if (!straightCorner || ![straightCorner containsObject:[NSNumber numberWithUnsignedInteger:vertex.count - 1]]) { target.x += sign(diff.x/beta)*beta; - } else { target.y += sign(diff.y/beta)*beta; } - [path moveToPoint:target]; + CGPathMoveToPoint(path, NULL, target.x, target.y); for (NSUInteger i = 0; i < vertex.count; i += 1) { - previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; - point = (vertex[i]).pointValue; - nextPoint = (vertex[(i+1)%vertex.count]).pointValue; + previousPoint = [vertex[(vertex.count+i-1)%vertex.count] pointValue]; + point = [vertex[i] pointValue]; + nextPoint = [vertex[(i+1)%vertex.count] pointValue]; target = point; - control1 = point; - diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { + if (straightCorner && [straightCorner containsObject:[NSNumber numberWithUnsignedInteger:i]]) { + CGPathAddLineToPoint(path, NULL, target.x, target.y); + } else { + control1 = point; + diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); target.x -= sign(diff.x/beta)*beta; control1.x -= sign(diff.x/beta)*alpha; - } else { target.y -= sign(diff.y/beta)*beta; control1.y -= sign(diff.y/beta)*alpha; - } - [path lineToPoint:target]; - target = point; - control2 = point; - diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); - if (ABS(diff.x) > ABS(diff.y)) { + + CGPathAddLineToPoint(path, NULL, target.x, target.y); + target = point; + control2 = point; + diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); control2.x += sign(diff.x/beta)*alpha; target.x += sign(diff.x/beta)*beta; - } else { control2.y += sign(diff.y/beta)*alpha; target.y += sign(diff.y/beta)*beta; + + CGPathAddCurveToPoint(path, NULL, control1.x, control1.y, control2.x, control2.y, target.x, target.y); } - [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; } - [path closePath]; + CGPathCloseSubpath(path); return path; } @@ -386,10 +372,10 @@ - (void)drawViewWith:(NSRange)hilightedRange void xyTranslation(NSMutableArray *shape, NSPoint direction) { for (NSUInteger i = 0; i < shape.count; i += 1) { - NSPoint point = (shape[i]).pointValue; + NSPoint point = [shape[i] pointValue]; point.x += direction.x; point.y += direction.y; - shape[i] = @(point); + [shape replaceObjectAtIndex:i withObject:@(point)]; } } @@ -400,27 +386,33 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _text.layoutManagers[0]; - NSTextContainer *textContainer = layoutManager.textContainers[0]; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; + NSRange firstLineRange = NSMakeRange(NSNotFound, 0); + NSRect firstLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&firstLineRange]; + NSRange lastLineRange = NSMakeRange(NSNotFound, 0); + NSRect lastLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&lastLineRange]; + *leadingRect = NSZeroRect; *bodyRect = boundingRect; *trailingRect = NSZeroRect; - if (boundingRect.origin.x <= 1 && fullRangeInBoundingRect.location < glyphRange.location) { - *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(fullRangeInBoundingRect.location, glyphRange.location-fullRangeInBoundingRect.location) inTextContainer:textContainer]; + // Multiline, not starting from beginning + if (boundingRect.origin.x <= 1 && firstLineRange.location < glyphRange.location) { + *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(firstLineRange.location, glyphRange.location-firstLineRange.location) inTextContainer:textContainer]; if (!nearEmptyRect(*leadingRect)) { bodyRect->size.height -= leadingRect->size.height; bodyRect->origin.y += leadingRect->size.height; } double rightEdge = NSMaxX(*leadingRect); leadingRect->origin.x = rightEdge; - leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; + leadingRect->size.width = NSMaxX(*bodyRect) - rightEdge; } - if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { + // Has trainling characters + if (NSMaxRange(lastLineRange) > NSMaxRange(glyphRange)) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) + NSMakeRange(NSMaxRange(glyphRange), NSMaxRange(lastLineRange)-NSMaxRange(glyphRange)) inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; @@ -428,29 +420,13 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe double leftEdge = NSMinX(*trailingRect); trailingRect->origin.x = bodyRect->origin.x; trailingRect->size.width = leftEdge - bodyRect->origin.x; - } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { - *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; - if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { - *trailingRect = NSZeroRect; - } else if (!nearEmptyRect(*trailingRect)) { - bodyRect->size.height -= trailingRect->size.height; - } - } - NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; - lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; - NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; - NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; - while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { - lastLineRange.length -= 1; - glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + // Has no trainling charcater } - if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { - if (!nearEmptyRect(*trailingRect)) { - *trailingRect = lastLineRect; - } else { - *bodyRect = lastLineRect; - } + + if ((!nearEmptyRect(*leadingRect) && bodyRect->size.height < leadingRect->size.height/2) || (!nearEmptyRect(*trailingRect) && bodyRect->size.height < trailingRect->size.height/2)) { + *bodyRect = NSZeroRect; } + NSSize edgeInset = self.currentTheme.edgeInset; leadingRect->origin.x += edgeInset.width; leadingRect->origin.y += edgeInset.height; @@ -471,20 +447,20 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], bodyVertex[1]]; + return @[bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { NSArray * trailingVertex = rectVertex(trailingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], bodyVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3], bodyVertex[0]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], leadingVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1], bodyVertex[0]]; } else { return @[]; } @@ -493,7 +469,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe // If the point is outside the innerBox, will extend to reach the outerBox void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerBorder) { for (NSUInteger i = 0; i < vertex.count; i += 1){ - NSPoint point = (vertex[i]).pointValue; + NSPoint point = [vertex[i] pointValue]; if (point.x < innerBorder.origin.x) { point.x = outerBorder.origin.x; } else if (point.x > innerBorder.origin.x+innerBorder.size.width) { @@ -504,18 +480,64 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } else if (point.y > innerBorder.origin.y+innerBorder.size.height) { point.y = outerBorder.origin.y+outerBorder.size.height; } - vertex[i] = @(point); + [vertex replaceObjectAtIndex:i withObject:@(point)]; + } +} + +CGPoint direction(CGPoint diff) { + if (diff.y == 0 && diff.x > 0) { + return NSMakePoint(0, 1); + } else if (diff.y == 0 && diff.x < 0) { + return NSMakePoint(0, -1); + } else if (diff.x == 0 && diff.y > 0) { + return NSMakePoint(-1, 0); + } else if (diff.x == 0 && diff.y < 0) { + return NSMakePoint(1, 0); + } else { + return NSMakePoint(0, 0); + } +} + +CAShapeLayer *shapeFromPath(CGPathRef path) { + CAShapeLayer *layer = [CAShapeLayer layer]; + layer.path = path; + layer.fillRule = kCAFillRuleEvenOdd; + return layer; +} + +// Assumes clockwise iteration +void enlarge(NSMutableArray *vertex, CGFloat by) { + if (by != 0) { + NSPoint previousPoint; + NSPoint point; + NSPoint nextPoint; + NSArray *original = [[NSArray alloc] initWithArray:vertex]; + NSPoint newPoint; + NSPoint displacement; + for (NSUInteger i = 0; i < original.count; i += 1){ + previousPoint = [original[(original.count+i-1)%original.count] pointValue]; + point = [original[i] pointValue]; + nextPoint = [original[(i+1)%original.count] pointValue]; + newPoint = point; + displacement = direction(NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y)); + newPoint.x += by * displacement.x; + newPoint.y += by * displacement.y; + displacement = direction(NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y)); + newPoint.x += by * displacement.x; + newPoint.y += by * displacement.y; + [vertex replaceObjectAtIndex:i withObject:@(newPoint)]; + } } } // Add gap between horizontal candidates -- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { - if (_highlightedRange.location+_highlightedRange.length == _text.length) { +- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect range:(NSRange)highlightedRange { + if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; + rect->size.width += _seperatorWidth; rect->origin.x -= _seperatorWidth / 2; } - } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + } else if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { if (!nearEmptyRect(*rect)) { rect->size.width += _seperatorWidth / 2; } @@ -527,220 +549,292 @@ - (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { } } +void removeCorner(NSMutableArray *highlightedPoints, NSMutableSet *rightCorners, NSRect containingRect) { + if (highlightedPoints && rightCorners) { + NSSet *originalRightCorners = [[NSSet alloc] initWithSet:rightCorners]; + for (NSNumber *cornerIndex in originalRightCorners) { + NSUInteger index = cornerIndex.unsignedIntegerValue; + NSPoint corner = [highlightedPoints[index] pointValue]; + CGFloat dist = MIN(NSMaxY(containingRect) - corner.y, corner.y - NSMinY(containingRect)); + if (dist < 1e-2) { + [rightCorners removeObject:cornerIndex]; + } + } + } +} + +- (void) linearMultilineForRect:(NSRect)bodyRect leadingRect:(NSRect)leadingRect trailingRect:(NSRect)trailingRect points1:(NSMutableArray **)highlightedPoints points2:(NSMutableArray **)highlightedPoints2 rightCorners:(NSMutableSet **)rightCorners rightCorners2:(NSMutableSet **)rightCorners2 { + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + *highlightedPoints = [rectVertex(leadingRect) mutableCopy]; + *highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(2), @(3), nil]; + } else { + *highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { + if (NSMaxX(trailingRect) < NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), @(4), @(5), nil]; + } else if (NSMaxX(trailingRect) >= NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; + } + } + } + if ([*highlightedPoints2 count] > 0) { + *rightCorners2 = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; + } +} + +- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect extraExpansion:(CGFloat)extraExpansion { + NSRect currentContainingRect = containingRect; + currentContainingRect.size.width += extraExpansion * 2; + currentContainingRect.size.height += extraExpansion * 2; + currentContainingRect.origin.x -= extraExpansion; + currentContainingRect.origin.y -= extraExpansion; + + CGFloat halfLinespace = theme.linespace / 2; + NSRect innerBox = backgroundRect; + innerBox.size.width -= (theme.edgeInset.width + 1) * 2 - 2 * extraExpansion; + innerBox.origin.x += theme.edgeInset.width + 1 - extraExpansion; + innerBox.size.height += 2 * extraExpansion; + innerBox.origin.y -= extraExpansion; + if (_preeditRange.length == 0) { + innerBox.origin.y += theme.edgeInset.height + 1; + innerBox.size.height -= (theme.edgeInset.height + 1) * 2; + } else { + innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; + innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + } + innerBox.size.height -= halfLinespace; + NSRect outerBox = backgroundRect; + outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; + outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; + outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; + outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; + + double effectiveRadius = MAX(0, theme.hilitedCornerRadius + 2 * extraExpansion / theme.hilitedCornerRadius * MAX(0, theme.cornerRadius - theme.hilitedCornerRadius)); + CGMutablePathRef path = CGPathCreateMutable(); + + if (theme.linear){ + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + + [self addGapBetweenHorizontalCandidates:&leadingRect range:highlightedRange]; + [self addGapBetweenHorizontalCandidates:&bodyRect range:highlightedRange]; + [self addGapBetweenHorizontalCandidates:&trailingRect range:highlightedRange]; + + NSMutableArray *highlightedPoints; + NSMutableArray *highlightedPoints2; + NSMutableSet *rightCorners; + NSMutableSet *rightCorners2; + [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPoints points2:&highlightedPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; + + xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); + xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); + // Expand the boxes to reach proper border + enlarge(highlightedPoints, extraExpansion); + expand(highlightedPoints, innerBox, outerBox); + removeCorner(highlightedPoints, rightCorners, currentContainingRect); + + path = drawSmoothLines(highlightedPoints, rightCorners, 0.3*effectiveRadius, 1.4*effectiveRadius); + if (highlightedPoints2.count > 0) { + enlarge(highlightedPoints2, extraExpansion); + expand(highlightedPoints2, innerBox, outerBox); + removeCorner(highlightedPoints2, rightCorners2, currentContainingRect); + CGPathRef path2 = drawSmoothLines(highlightedPoints2, rightCorners2, 0.3*effectiveRadius, 1.4*effectiveRadius); + CGPathAddPath(path, NULL, path2); + } + } else { + NSRect highlightedRect = [self contentRectForRange:highlightedRange]; + highlightedRect.size.width = backgroundRect.size.width; + highlightedRect.size.height += theme.linespace; + highlightedRect.origin = NSMakePoint(backgroundRect.origin.x, highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); + if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + } + if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + if (_preeditRange.length == 0) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + } else { + highlightedRect.size.height += theme.hilitedCornerRadius / 2; + highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + } + } + NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; + enlarge(highlightedPoints, extraExpansion); + expand(highlightedPoints, innerBox, outerBox); + path = drawSmoothLines(highlightedPoints, nil, 0.3*effectiveRadius, 1.4*effectiveRadius); + } + return path; +} + // All draws happen here - (void)drawRect:(NSRect)dirtyRect { - NSBezierPath *backgroundPath; - NSBezierPath *borderPath; - NSBezierPath *highlightedPath; - NSBezierPath *highlightedPath2; - NSBezierPath *highlightedPreeditPath; - NSBezierPath *highlightedPreeditPath2; - NSBezierPath *preeditPath; + CGPathRef backgroundPath = CGPathCreateMutable(); + CGPathRef highlightedPath = CGPathCreateMutable(); + CGMutablePathRef candidatePaths = CGPathCreateMutable(); + CGMutablePathRef highlightedPreeditPath = CGPathCreateMutable(); + CGPathRef preeditPath = CGPathCreateMutable(); SquirrelTheme * theme = self.currentTheme; - NSRect textField = dirtyRect; - textField.origin.y += theme.edgeInset.height; - textField.origin.x += theme.edgeInset.width; + NSPoint textFieldOrigin = dirtyRect.origin; + textFieldOrigin.y += theme.edgeInset.height; + textFieldOrigin.x += theme.edgeInset.width; // Draw preedit Rect NSRect backgroundRect = dirtyRect; + NSRect containingRect = dirtyRect; + containingRect.size.height -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; + containingRect.size.width -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; + containingRect.origin.x += theme.hilitedCornerRadius + theme.borderWidth; + containingRect.origin.y += theme.hilitedCornerRadius + theme.borderWidth; // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = textField.size.width; + preeditRect.size.width = backgroundRect.size.width; preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); - if (_highlightedRange.length == 0) { + preeditRect.origin = backgroundRect.origin; + if (_candidateRanges.count == 0) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; } if (theme.preeditBackgroundColor != nil) { - preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); + preeditPath = drawSmoothLines(rectVertex(preeditRect), nil, 0, 0); } } // Draw highlighted Rect - if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; - } - NSRect outerBox = backgroundRect; - outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; - - CGFloat halfLinespace = theme.linespace / 2; - if (theme.linear){ - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - - [self addGapBetweenHorizontalCandidates:&leadingRect]; - [self addGapBetweenHorizontalCandidates:&bodyRect]; - [self addGapBetweenHorizontalCandidates:&trailingRect]; - - NSMutableArray *highlightedPoints; - NSMutableArray *highlightedPoints2; - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - } - - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - innerBox.size.height -= halfLinespace; - // Expand the boxes to reach proper border - expand(highlightedPoints, innerBox, outerBox); - expand(highlightedPoints2, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - if (highlightedPoints2.count > 0) { - highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + NSRange candidateRange = [_candidateRanges[i] rangeValue]; + if (i == _hilightedIndex) { + // Draw highlighted Rect + if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { + highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:0]; } } else { - NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; - highlightedRect.size.width = textField.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, - highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } + // Draw other highlighted Rect + if (candidateRange.length > 0 && theme.candidateBackColor != nil) { + CGPathRef candidatePath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:theme.surroundingExtraExpansion]; + CGPathAddPath(candidatePaths, NULL, candidatePath); } - NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - expand(highlightedPoints, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - NSRect innerBox = preeditRect; innerBox.size.width -= (theme.edgeInset.width + 1) * 2; innerBox.origin.x += theme.edgeInset.width + 1; innerBox.origin.y += theme.edgeInset.height + 1; - if (_highlightedRange.length == 0) { + if (_candidateRanges.count == 0) { innerBox.size.height -= (theme.edgeInset.height + 1) * 2; } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; + innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; } NSRect outerBox = preeditRect; - outerBox.size.height -= theme.hilitedCornerRadius; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2; - + outerBox.size.height -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; + outerBox.origin.y += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; + + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - } - // Expand the boxes to reach proper border + NSMutableSet *rightCorners; + NSMutableSet *rightCorners2; + [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPreeditPoints points2:&highlightedPreeditPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; + expand(highlightedPreeditPoints, innerBox, outerBox); - expand(highlightedPreeditPoints2, innerBox, outerBox); - highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + removeCorner(highlightedPreeditPoints, rightCorners, containingRect); + highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, rightCorners, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + expand(highlightedPreeditPoints2, innerBox, outerBox); + removeCorner(highlightedPreeditPoints2, rightCorners2, containingRect); + CGPathRef highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, rightCorners2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + CGPathAddPath(highlightedPreeditPath, NULL, highlightedPreeditPath2); } } [NSBezierPath setDefaultLineWidth:0]; - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); - _shape.path = backgroundPath.quartzPath; - // Nothing should extend beyond backgroundPath - borderPath = [backgroundPath copy]; - [borderPath addClip]; - borderPath.lineWidth = theme.borderWidth; - -// This block of code enables independent transparencies in highlighted colour and background colour. -// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. -#if 0 - // Calculate intersections. - if (![highlightedPath isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath copy]]; - if (![highlightedPath2 isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath2 copy]]; - } - } - - if (![preeditPath isEmpty]) { - [backgroundPath appendBezierPath:[preeditPath copy]]; - } - - if (![highlightedPreeditPath isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath copy]]; - } - if (![highlightedPreeditPath2 isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath2 copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath2 copy]]; + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), nil, theme.cornerRadius*0.3, theme.cornerRadius*1.4); + _shape.path = CGPathCreateMutableCopy(backgroundPath); + + [self.layer setSublayers: NULL]; + CGMutablePathRef backPath = CGPathCreateMutableCopy(backgroundPath); + if (!CGPathIsEmpty(preeditPath)) { + CGPathAddPath(backPath, NULL, preeditPath); + } + if (theme.mutualExclusive) { + if (!CGPathIsEmpty(highlightedPath)) { + CGPathAddPath(backPath, NULL, highlightedPath); + } + if (!CGPathIsEmpty(candidatePaths)) { + CGPathAddPath(backPath, NULL, candidatePaths); + } + } + CAShapeLayer *panelLayer = shapeFromPath(backPath); + panelLayer.fillColor = theme.backgroundColor.CGColor; + CAShapeLayer *panelLayerMask = shapeFromPath(backgroundPath); + panelLayer.mask = panelLayerMask; + [self.layer addSublayer: panelLayer]; + + if (theme.preeditBackgroundColor && !CGPathIsEmpty(preeditPath)) { + CAShapeLayer *layer = shapeFromPath(preeditPath); + layer.fillColor = theme.preeditBackgroundColor.CGColor; + CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); + if (theme.mutualExclusive && !CGPathIsEmpty(highlightedPreeditPath)) { + CGPathAddPath(maskPath, NULL, highlightedPreeditPath); + } + CAShapeLayer *mask = shapeFromPath(maskPath); + layer.mask = mask; + [panelLayer addSublayer: layer]; + } + if (theme.borderWidth > 0 && theme.borderColor) { + CAShapeLayer *borderLayer = shapeFromPath(backgroundPath); + borderLayer.lineWidth = theme.borderWidth * 2; + borderLayer.strokeColor = theme.borderColor.CGColor; + borderLayer.fillColor = NULL; + [panelLayer addSublayer: borderLayer]; + } + if (theme.highlightedPreeditColor && !CGPathIsEmpty(highlightedPreeditPath)) { + CAShapeLayer *layer = shapeFromPath(highlightedPreeditPath); + layer.fillColor = theme.highlightedPreeditColor.CGColor; + [panelLayer addSublayer: layer]; + } + if (theme.candidateBackColor && !CGPathIsEmpty(candidatePaths)) { + CAShapeLayer *layer = shapeFromPath(candidatePaths); + layer.fillColor = theme.candidateBackColor.CGColor; + [panelLayer addSublayer: layer]; + } + if (theme.highlightedBackColor && !CGPathIsEmpty(highlightedPath)) { + CAShapeLayer *layer = shapeFromPath(highlightedPath); + layer.fillColor = theme.highlightedBackColor.CGColor; + if (theme.shadowSize > 0) { + CAShapeLayer *shadowLayer = [CAShapeLayer layer]; + shadowLayer.shadowColor = NSColor.blackColor.CGColor; + shadowLayer.shadowOffset = NSMakeSize(theme.shadowSize/2, (theme.vertical ? -1 : 1) * theme.shadowSize/2); + shadowLayer.shadowPath = highlightedPath; + shadowLayer.shadowRadius = theme.shadowSize; + shadowLayer.shadowOpacity = 0.2; + CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); + CGPathAddPath(maskPath, NULL, highlightedPath); + if (!CGPathIsEmpty(preeditPath)) { + CGPathAddPath(maskPath, NULL, preeditPath); } + CAShapeLayer *shadowLayerMask = shapeFromPath(maskPath); + shadowLayer.mask = shadowLayerMask; + [layer addSublayer: shadowLayer]; } + [panelLayer addSublayer: layer]; } - [backgroundPath setWindingRule:NSEvenOddWindingRule]; - [preeditPath setWindingRule:NSEvenOddWindingRule]; -#endif - - [theme.backgroundColor setFill]; - [backgroundPath fill]; - if (theme.preeditBackgroundColor && !preeditPath.empty) { - [theme.preeditBackgroundColor setFill]; - [preeditPath fill]; - } - if (theme.highlightedStripColor && !highlightedPath.empty) { - [theme.highlightedStripColor setFill]; - [highlightedPath fill]; - if (!highlightedPath2.empty) { - [highlightedPath2 fill]; - } - } - if (theme.highlightedPreeditColor && !highlightedPreeditPath.empty) { - [theme.highlightedPreeditColor setFill]; - [highlightedPreeditPath fill]; - if (!highlightedPreeditPath2.empty) { - [highlightedPreeditPath2 fill]; - } - } - - if (theme.borderColor && (theme.borderWidth > 0)) { - [theme.borderColor setStroke]; - [borderPath stroke]; - } - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + [_textView setTextContainerInset:NSMakeSize(textFieldOrigin.x, textFieldOrigin.y)]; } @end @@ -773,42 +867,6 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -CGFloat minimumHeight(NSDictionary *attribute) { - const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; - const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; - return minimumHeight; -} - -// Use this method to convert charcters to upright position -// Based on the width of the chacter, relative font size matters -void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { - NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; - double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; - // Use the width of the character to determin if they should be upright in vertical writing mode. - // Adjust font base line for better alignment. - const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; - const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; - const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; - const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; - stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; - NSUInteger i = stringRange.location; - while (i < stringRange.location+stringRange.length) { - NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; - i = range.location + range.length; - NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - // Also adjust the baseline so upright and lying charcters are properly aligned - if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { - [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; - NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - CGFloat widthDiff = charRect.size.width-cjkChar.size.width; - CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; - } else { - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; - } - } -} - void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -823,6 +881,20 @@ void fixDefaultFont(NSMutableAttributedString *text) { } } +NSAttributedString *insert(NSString *separator, NSAttributedString *betweenText) { + NSRange range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:0]; + NSAttributedString *attributedSeperator = [[NSAttributedString alloc] initWithString:separator attributes:[betweenText attributesAtIndex:0 effectiveRange:nil]]; + NSUInteger i = NSMaxRange(range); + NSMutableAttributedString *workingString = [[betweenText attributedSubstringFromRange:range] mutableCopy]; + while (i < betweenText.length) { + range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:i]; + [workingString appendAttributedString:attributedSeperator]; + [workingString appendAttributedString:[betweenText attributedSubstringFromRange:range]]; + i = NSMaxRange(range); + } + return workingString; +} + + (NSColor *)secondaryTextColor { if(@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -834,6 +906,7 @@ + (NSColor *)secondaryTextColor { - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; theme.native = YES; + theme.memorizeSize = YES; theme.candidateFormat = kDefaultCandidateFormat; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -867,8 +940,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -903,7 +976,8 @@ - (instancetype)init { [contentView addSubview:_back]; } [contentView addSubview:_view]; - + [contentView addSubview:_view.textView]; + self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; if (@available(macOS 10.14, *)) { @@ -929,6 +1003,15 @@ - (void)getCurrentScreen { } } +- (CGFloat)getMaxTextWidth:(SquirrelTheme *)theme { + NSFont *currentFont = theme.attrs[NSFontAttributeName]; + CGFloat fontScale = currentFont.pointSize / 12; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + return theme.vertical + ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; +} + // Get the window size, the windows will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; @@ -942,28 +1025,20 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = _view.text.size.width; - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - CGFloat maxTextWidth = theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - if (textWidth > maxTextWidth) { - textWidth = maxTextWidth; - } - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); + CGFloat textWidth = [self getMaxTextWidth:theme]; + CGFloat maxTextHeight = theme.vertical ? _screenRect.size.width - theme.edgeInset.width * 2 : _screenRect.size.height - theme.edgeInset.height * 2; + _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || - (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { + if (theme.memorizeSize && ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect)))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); + _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); } } @@ -1011,14 +1086,17 @@ - (void)show { [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { - self.contentView.boundsRotation = -90.0; + self.contentView.boundsRotation = -90; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; } else { self.contentView.boundsRotation = 0; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; } BOOL translucency = theme.translucency; [_view setFrame:self.contentView.bounds]; + [_view.textView setFrame:self.contentView.bounds]; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; @@ -1069,6 +1147,8 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; + [self getCurrentScreen]; + CGFloat maxTextWidth = [self getMaxTextWidth:theme]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; @@ -1101,13 +1181,8 @@ - (void)showPreedit:(NSString *)preedit } [text appendAttributedString:line]; - NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; - if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, line.length)); - paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); - } [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit + value:theme.preeditParagraphStyle range:NSMakeRange(0, text.length)]; _preeditRange = NSMakeRange(0, text.length); @@ -1119,17 +1194,25 @@ - (void)showPreedit:(NSString *)preedit candidateStartPos = text.length; } - NSRange highlightedRange = NSMakeRange(NSNotFound, 0); + NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; // candidates NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; - NSDictionary *labelAttrs = - (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; - NSDictionary *commentAttrs = - (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; + NSDictionary *attrs; + NSDictionary *labelAttrs; + NSDictionary *commentAttrs; + if (i == index) { + attrs = theme.highlightedAttrs; + labelAttrs = theme.labelHighlightedAttrs; + commentAttrs = theme.commentHighlightedAttrs; + } else { + attrs = theme.attrs; + labelAttrs = theme.labelAttrs; + commentAttrs = theme.commentAttrs; + } + CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { @@ -1152,25 +1235,31 @@ - (void)showPreedit:(NSString *)preedit initWithString:labelString attributes:labelAttrs]]; // get the label size for indent - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(0, line.length)); - } if (!theme.linear) { - labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + NSMutableAttributedString *str = [line mutableCopy]; + if (theme.vertical) { + [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; + } + labelWidth = [str boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]]; + NSAttributedString *candidateAttributedString = [[NSAttributedString alloc] + initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]; + CGFloat candidateWidth = [candidateAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + if (candidateWidth <= maxTextWidth * 0.2) { + // Unicode Word Joiner + candidateAttributedString = insert(@"\u2060", candidateAttributedString); + } + + [line appendAttributedString:candidateAttributedString]; + // Use left-to-right marks to prevent right-to-left text from changing the // layout of non-candidate text. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); - } if (theme.suffixLabelFormat != nil) { NSString *labelString; @@ -1186,58 +1275,63 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; labelString = [NSString stringWithFormat:labelFormat, i+1]; } - NSUInteger suffixLabelStart = line.length; [line appendAttributedString: [[NSAttributedString alloc] initWithString:labelString attributes:labelAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); - } } if (i < comments.count && [comments[i] length] != 0) { - NSUInteger commentStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; + CGFloat candidateAndLabelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; NSString *comment = comments[i]; + NSAttributedString *commentAttributedString = [[NSAttributedString alloc] + initWithString:comment.precomposedStringWithCanonicalMapping + attributes:commentAttrs]; + CGFloat commentWidth = [commentAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + if (commentWidth <= maxTextWidth * 0.2) { + // Unicode Word Joiner + commentAttributedString = insert(@"\u2060", commentAttributedString); + } + + NSString *commentSeparator; + if (candidateAndLabelWidth + commentWidth <= maxTextWidth * 0.3) { + // Non-Breaking White Space + commentSeparator = @"\u00A0"; + } else { + commentSeparator = @" "; + } [line appendAttributedString:[[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping + initWithString:commentSeparator attributes:commentAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); - } + [line appendAttributedString:commentAttributedString]; } NSAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:attrs]; - _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; + + NSMutableAttributedString *str = [separator mutableCopy]; + if (theme.vertical) { + [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; + } + _view.seperatorWidth = [str boundingRectWithSize:NSZeroSize options:0].size.width; - NSMutableParagraphStyle *paragraphStyleCandidate; + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (i == 0) { - NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; - firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - paragraphStyleCandidate = firstParagraphStyle; + paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; } else { - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; [text appendAttributedString:separator]; } if (theme.linear) { paragraphStyleCandidate.lineSpacing = theme.linespace; } - if (theme.vertical) { - paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); - } paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; - if (i == index) { - highlightedRange = NSMakeRange(text.length, line.length); - } + NSRange candidateRange = NSMakeRange(text.length, line.length); + [candidateRanges addObject: [NSValue valueWithRange:candidateRange]]; [text appendAttributedString:line]; } @@ -1245,8 +1339,13 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(text); // text done! - [_view setText:text]; - [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view.textView.textStorage setAttributedString:text]; + if (theme.vertical) { + _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; + } else { + _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; + } + [_view drawViewWith:candidateRanges hilightedIndex:index preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; } @@ -1256,13 +1355,19 @@ - (void)updateStatus:(NSString *)message { - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.attrs]; + [text addAttribute:NSParagraphStyleAttributeName + value:theme.paragraphStyle + range:NSMakeRange(0, text.length)]; + fixDefaultFont(text); + [_view.textView.textStorage setAttributedString:text]; if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, text.length)); + _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; + } else { + _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; } - [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:[[NSArray alloc] init] hilightedIndex:0 preeditRange:emptyRange highlightedPreeditRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1361,22 +1466,30 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; + BOOL mutualExclusive = [config getBool:@"style/mutual_exclusive"]; + NSNumber *memorizeSizeConfig = [config getOptionalBool:@"style/memorize_size"]; + if (memorizeSizeConfig) { + theme.memorizeSize = memorizeSizeConfig.boolValue; + } + NSString *candidateFormat = [config getString:@"style/candidate_format"]; - NSString *fontName = [config getString:@"style/font_face"]; - NSInteger fontSize = [config getDouble:@"style/font_point"]; + CGFloat fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; + CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; - CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); + CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; + NSNumber *alphaValue = [config getOptionalDouble:@"style/alpha"]; + CGFloat alpha = alphaValue ? fmin(fmax(alphaValue.doubleValue, 0.0), 1.0) : 1.0; CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; + CGFloat surroundingExtraExpansion = [config getDouble:@"style/surrounding_extra_expansion"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; + CGFloat shadowSize = fmax(0,[config getDouble:@"style/shadow_size"]); NSColor *backgroundColor; NSColor *borderColor; @@ -1389,6 +1502,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSColor *candidateTextColor; NSColor *highlightedCandidateTextColor; NSColor *highlightedCandidateBackColor; + NSColor *candidateBackColor; NSColor *commentTextColor; NSColor *highlightedCommentTextColor; @@ -1423,6 +1537,16 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo // if not otherwise specified, candidate text is also rendered in this color. candidateTextColor = textColor; } + candidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/label_color"]]; + highlightedCandidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; + if (!highlightedCandidateLabelColor) { + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' + // are both valid + highlightedCandidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; + } highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; if (highlightedCandidateTextColor == nil) { @@ -1433,6 +1557,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (highlightedCandidateBackColor == nil) { highlightedCandidateBackColor = highlightedBackColor; } + candidateBackColor = + [config getColor:[prefix stringByAppendingString:@"/candidate_back_color"]]; commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; highlightedCommentTextColor = @@ -1459,6 +1585,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } + NSNumber *mutualExclusiveOverridden = + [config getOptionalBool:[prefix stringByAppendingString:@"/mutual_exclusive"]]; + if (mutualExclusiveOverridden) { + mutualExclusive = mutualExclusiveOverridden.boolValue; + } NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { @@ -1495,22 +1626,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSizeOverridden) { commentFontSize = commentFontSizeOverridden.integerValue; } - NSColor *candidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_color"]]; - if (candidateLabelColorOverridden) { - candidateLabelColor = candidateLabelColorOverridden; - } - NSColor *highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (!highlightedCandidateLabelColorOverridden) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' - // are both valid - highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; - } - if (highlightedCandidateLabelColorOverridden) { - highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; - } NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { @@ -1526,6 +1641,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (hilitedCornerRadiusOverridden) { hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; } + NSNumber *surroundingExtraExpansionOverridden = + [config getOptionalDouble:[prefix stringByAppendingString:@"/surrounding_extra_expansion"]]; + if (surroundingExtraExpansionOverridden) { + surroundingExtraExpansion = surroundingExtraExpansionOverridden.doubleValue; + } NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { @@ -1551,6 +1671,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } + NSNumber *shadowSizeOverridden = + [config getOptionalDouble:[prefix stringByAppendingString:@"/shadow_size"]]; + if (shadowSizeOverridden) { + shadowSize = shadowSizeOverridden.doubleValue; + } } if (fontSize == 0) { // default size @@ -1662,8 +1787,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; - labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; + labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; labelHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateLabelColor; commentAttrs[NSForegroundColorAttributeName] = commentTextColor; commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; @@ -1671,8 +1796,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -1683,26 +1808,30 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle:preeditParagraphStyle]; [theme setBackgroundColor:backgroundColor - highlightedStripColor:highlightedCandidateBackColor + highlightedBackColor:highlightedCandidateBackColor + candidateBackColor:candidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; NSSize edgeInset; if (vertical) { - edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); + edgeInset = NSMakeSize(borderHeight + cornerRadius, borderWidth + cornerRadius); } else { - edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); + edgeInset = NSMakeSize(borderWidth + cornerRadius, borderHeight + cornerRadius); } [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius + srdExtraExpansion:surroundingExtraExpansion + shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing - alpha:(alpha == 0 ? 1.0 : alpha) + alpha:alpha translucency:translucency + mutualExclusive:mutualExclusive linear:linear vertical:vertical inlinePreedit:inlinePreedit From a5497541e384584e9ddb20797691a38042793f8c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 1 Feb 2023 04:56:49 +0100 Subject: [PATCH 014/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index a004f59b0..e698f7e65 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit a004f59b099cdc2ea0624c3ec2cc1a01f43e3cff +Subproject commit e698f7e65f2c48fbe24f9eb31acc98af6815a9c1 From 7f37078f1439da14cf7ea5307e2b2b9abd0ee929 Mon Sep 17 00:00:00 2001 From: LEO Yoon-Tsaw Date: Sun, 29 Jan 2023 10:24:47 -0500 Subject: [PATCH 015/164] Adopt NSTextView along with new UI features --- SquirrelPanel.m | 1043 ++++++++++++++++++++++++++--------------------- 1 file changed, 583 insertions(+), 460 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index f61b0b5ce..8ccda4ea0 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,76 +3,35 @@ #import "SquirrelConfig.h" #import -@implementation NSBezierPath (BezierPathQuartzUtilities) -// This method works only in OS X v10.2 and later. -- (CGPathRef)quartzPath { - NSInteger i, numElements; - // Need to begin a path here. - CGPathRef immutablePath = NULL; - - // Then draw the path elements. - numElements = [self elementCount]; - if (numElements > 0) { - CGMutablePathRef path = CGPathCreateMutable(); - NSPoint points[3]; - BOOL didClosePath = YES; - for (i = 0; i < numElements; i++) { - switch ([self elementAtIndex:i associatedPoints:points]) { - case NSMoveToBezierPathElement: - CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); - break; - case NSLineToBezierPathElement: - CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); - didClosePath = NO; - break; - case NSCurveToBezierPathElement: - CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, - points[1].x, points[1].y, - points[2].x, points[2].y); - didClosePath = NO; - break; - case NSClosePathBezierPathElement: - CGPathCloseSubpath(path); - didClosePath = YES; - break; - } - } - - // Be sure the path is closed or Quartz may not do valid hit detection. - if (!didClosePath) { - CGPathCloseSubpath(path); - } - immutablePath = CGPathCreateCopy(path); - CGPathRelease(path); - } - return immutablePath; -} -@end - static const CGFloat kOffsetHeight = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kDefaultCandidateFormat = @"%c.\u00A0%@"; @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL native; +@property(nonatomic, assign) BOOL memorizeSize; @property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSColor *highlightedStripColor; +@property(nonatomic, strong, readonly) NSColor *highlightedBackColor; +@property(nonatomic, strong, readonly) NSColor *candidateBackColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @property(nonatomic, strong, readonly) NSColor *borderColor; @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; +@property(nonatomic, readonly) CGFloat surroundingExtraExpansion; +@property(nonatomic, readonly) CGFloat shadowSize; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; +@property(nonatomic, readonly) BOOL mutualExclusive; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -94,32 +53,36 @@ @interface SquirrelTheme : NSObject - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedStripColor:(NSColor *)highlightedStripColor + highlightedBackColor:(NSColor *)highlightedBackColor + candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius + srdExtraExpansion:(CGFloat)surroundingExtraExpansion + shadowSize:(CGFloat)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(BOOL)translucency + mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate; -- (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; @@ -155,12 +118,14 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedStripColor:(NSColor *)highlightedStripColor + highlightedBackColor:(NSColor *)highlightedBackColor + candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; - _highlightedStripColor = highlightedStripColor; + _highlightedBackColor = highlightedBackColor; + _candidateBackColor = candidateBackColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; _borderColor = borderColor; @@ -168,23 +133,29 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(double)cornerRadius hilitedCornerRadius:(double)hilitedCornerRadius + srdExtraExpansion:(double)surroundingExtraExpansion + shadowSize:(double)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(CGFloat)alpha + alpha:(double)alpha translucency:(BOOL)translucency + mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; + _surroundingExtraExpansion = surroundingExtraExpansion; + _shadowSize = shadowSize; _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; _alpha = alpha; _translucency = translucency; + _mutualExclusive = mutualExclusive; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -192,17 +163,17 @@ - (void)setCornerRadius:(double)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { _attrs = attrs; - _labelAttrs = labelAttrs; _highlightedAttrs = highlightedAttrs; + _labelAttrs = labelAttrs; _labelHighlightedAttrs = labelHighlightedAttrs; _commentAttrs = commentAttrs; _commentHighlightedAttrs = commentHighlightedAttrs; @@ -220,8 +191,9 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextStorage *text; -@property(nonatomic, readonly) NSRange highlightedRange; +@property(nonatomic, readonly) NSTextView *textView; +@property(nonatomic, readonly) NSArray *candidateRanges; +@property(nonatomic, readonly) NSInteger hilightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRect contentRect; @@ -230,11 +202,10 @@ @interface SquirrelView : NSView @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; -- (BOOL)isFlipped; -- (void)setText:(NSAttributedString *)text; -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; +- (void) drawViewWith:(NSArray *)candidateRanges + hilightedIndex:(NSInteger)hilightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -271,45 +242,60 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } - // Use textStorage to store text and manage all text layout and draws + _textView = [[NSTextView alloc] initWithFrame:frameRect]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - _text = [[NSTextStorage alloc] init]; - [_text addLayoutManager:layoutManager]; - layoutManager.backgroundLayoutEnabled = YES; + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + [_textView replaceTextContainer:textContainer]; + _textView.layoutManager.backgroundLayoutEnabled = YES; _defaultTheme = [[SquirrelTheme alloc] init]; - _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } + _shape = [[CAShapeLayer alloc] init]; return self; } // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; + __block long actualWidth = 0; + [_textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { + NSRange range = [self.textView.layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + NSAttributedString *str = [self.textView.textStorage attributedSubstringFromRange:range]; + NSRange nonWhiteRange = [str.string rangeOfCharacterFromSet:NSCharacterSet.whitespaceAndNewlineCharacterSet.invertedSet options:NSBackwardsSearch]; + if (nonWhiteRange.location != NSNotFound) { + NSRange newRange = NSMakeRange(range.location, NSMaxRange(nonWhiteRange)); + NSRange newGlyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:newRange actualCharacterRange:NULL]; + CGFloat width = [self.textView.layoutManager boundingRectForGlyphRange:newGlyphRange inTextContainer:self.textView.textContainer].size.width; + if (width > actualWidth) { + actualWidth = width; + } + } + }]; + if (actualWidth > 0) { + rect.size.width = actualWidth; + } return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } -- (void)setText:(NSAttributedString *)text { - [_text setAttributedString:[text copy]]; -} - // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { - _highlightedRange = hilightedRange; +- (void) drawViewWith:(NSArray *)candidateRanges + hilightedIndex:(NSInteger)hilightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange { + _candidateRanges = candidateRanges; + _hilightedIndex = hilightedIndex; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; self.needsDisplay = YES; @@ -321,14 +307,15 @@ - (void)drawViewWith:(NSRange)hilightedRange return 1; } else if (number <= -2) { return -1; - }else { + } else { return number / 2; } } // Bezier cubic curve, which has continuous roundness -NSBezierPath *drawSmoothLines(NSArray *vertex, CGFloat alpha, CGFloat beta) { - NSBezierPath *path = [NSBezierPath bezierPath]; +CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * __nullable straightCorner, CGFloat alpha, CGFloat beta) { + beta = MAX(0.00001, beta); + CGMutablePathRef path = CGPathCreateMutable(); if (vertex.count < 1) return path; NSPoint previousPoint = [vertex[vertex.count-1] pointValue]; @@ -338,40 +325,39 @@ - (void)drawViewWith:(NSRange)hilightedRange NSPoint control2; NSPoint target = previousPoint; NSPoint diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { + if (!straightCorner || ![straightCorner containsObject:[NSNumber numberWithUnsignedInteger:vertex.count - 1]]) { target.x += sign(diff.x/beta)*beta; - } else { target.y += sign(diff.y/beta)*beta; } - [path moveToPoint:target]; + CGPathMoveToPoint(path, NULL, target.x, target.y); for (NSUInteger i = 0; i < vertex.count; i += 1) { previousPoint = [vertex[(vertex.count+i-1)%vertex.count] pointValue]; point = [vertex[i] pointValue]; nextPoint = [vertex[(i+1)%vertex.count] pointValue]; target = point; - control1 = point; - diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { + if (straightCorner && [straightCorner containsObject:[NSNumber numberWithUnsignedInteger:i]]) { + CGPathAddLineToPoint(path, NULL, target.x, target.y); + } else { + control1 = point; + diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); target.x -= sign(diff.x/beta)*beta; control1.x -= sign(diff.x/beta)*alpha; - } else { target.y -= sign(diff.y/beta)*beta; control1.y -= sign(diff.y/beta)*alpha; - } - [path lineToPoint:target]; - target = point; - control2 = point; - diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); - if (ABS(diff.x) > ABS(diff.y)) { + + CGPathAddLineToPoint(path, NULL, target.x, target.y); + target = point; + control2 = point; + diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); control2.x += sign(diff.x/beta)*alpha; target.x += sign(diff.x/beta)*beta; - } else { control2.y += sign(diff.y/beta)*alpha; target.y += sign(diff.y/beta)*beta; + + CGPathAddCurveToPoint(path, NULL, control1.x, control1.y, control2.x, control2.y, target.x, target.y); } - [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; } - [path closePath]; + CGPathCloseSubpath(path); return path; } @@ -399,58 +385,55 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle -- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _text.layoutManagers[0]; - NSTextContainer *textContainer = layoutManager.textContainers[0]; +- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect extraSurounding:(CGFloat)extraSurounding { + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; + NSRange firstLineRange = NSMakeRange(NSNotFound, 0); + [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&firstLineRange]; + NSRange lastLineRange = NSMakeRange(NSNotFound, 0); + [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&lastLineRange]; + *leadingRect = NSZeroRect; *bodyRect = boundingRect; *trailingRect = NSZeroRect; - if (boundingRect.origin.x <= 1 && fullRangeInBoundingRect.location < glyphRange.location) { - *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(fullRangeInBoundingRect.location, glyphRange.location-fullRangeInBoundingRect.location) inTextContainer:textContainer]; + + // Multiline, not starting from beginning + if (firstLineRange.location < lastLineRange.location && firstLineRange.location < glyphRange.location) { + *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(glyphRange.location, NSMaxRange(firstLineRange)-glyphRange.location) inTextContainer:textContainer]; if (!nearEmptyRect(*leadingRect)) { bodyRect->size.height -= leadingRect->size.height; bodyRect->origin.y += leadingRect->size.height; } - double rightEdge = NSMaxX(*leadingRect); - leadingRect->origin.x = rightEdge; - leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; } - if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { + // Multiline, has trainling characters + if (firstLineRange.location < lastLineRange.location && NSMaxRange(lastLineRange) > NSMaxRange(glyphRange)) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) + NSMakeRange(lastLineRange.location, NSMaxRange(glyphRange)-lastLineRange.location) inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; } - double leftEdge = NSMinX(*trailingRect); - trailingRect->origin.x = bodyRect->origin.x; - trailingRect->size.width = leftEdge - bodyRect->origin.x; - } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { - *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; - if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { - *trailingRect = NSZeroRect; - } else if (!nearEmptyRect(*trailingRect)) { - bodyRect->size.height -= trailingRect->size.height; - } } - NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; - lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; - NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; - NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; - while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { - lastLineRange.length -= 1; - glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + + if ((!nearEmptyRect(*leadingRect) && bodyRect->size.height < leadingRect->size.height/2) || (!nearEmptyRect(*trailingRect) && bodyRect->size.height < trailingRect->size.height/2)) { + *bodyRect = NSZeroRect; } - if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { - if (!nearEmptyRect(*trailingRect)) { - *trailingRect = lastLineRect; + + if (extraSurounding > 0) { + if (nearEmptyRect(*leadingRect) && nearEmptyRect(*trailingRect)) { + expandHighlightWidth(bodyRect, extraSurounding); } else { - *bodyRect = lastLineRect; + if (!(nearEmptyRect(*leadingRect))) { + expandHighlightWidth(leadingRect, extraSurounding); + } + if (!(nearEmptyRect(*trailingRect))) { + expandHighlightWidth(trailingRect, extraSurounding); + } } } + NSSize edgeInset = self.currentTheme.edgeInset; leadingRect->origin.x += edgeInset.width; leadingRect->origin.y += edgeInset.height; @@ -471,20 +454,20 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], bodyVertex[1]]; + return @[bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { NSArray * trailingVertex = rectVertex(trailingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], bodyVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3], bodyVertex[0]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], leadingVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1], bodyVertex[0]]; } else { return @[]; } @@ -508,239 +491,344 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } +CGPoint direction(CGPoint diff) { + if (diff.y == 0 && diff.x > 0) { + return NSMakePoint(0, 1); + } else if (diff.y == 0 && diff.x < 0) { + return NSMakePoint(0, -1); + } else if (diff.x == 0 && diff.y > 0) { + return NSMakePoint(-1, 0); + } else if (diff.x == 0 && diff.y < 0) { + return NSMakePoint(1, 0); + } else { + return NSMakePoint(0, 0); + } +} + +CAShapeLayer *shapeFromPath(CGPathRef path) { + CAShapeLayer *layer = [CAShapeLayer layer]; + layer.path = path; + layer.fillRule = kCAFillRuleEvenOdd; + return layer; +} + +// Assumes clockwise iteration +void enlarge(NSMutableArray *vertex, CGFloat by) { + if (by != 0) { + NSPoint previousPoint; + NSPoint point; + NSPoint nextPoint; + NSArray *original = [[NSArray alloc] initWithArray:vertex]; + NSPoint newPoint; + NSPoint displacement; + for (NSUInteger i = 0; i < original.count; i += 1){ + previousPoint = [original[(original.count+i-1)%original.count] pointValue]; + point = [original[i] pointValue]; + nextPoint = [original[(i+1)%original.count] pointValue]; + newPoint = point; + displacement = direction(NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y)); + newPoint.x += by * displacement.x; + newPoint.y += by * displacement.y; + displacement = direction(NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y)); + newPoint.x += by * displacement.x; + newPoint.y += by * displacement.y; + [vertex replaceObjectAtIndex:i withObject:@(newPoint)]; + } + } +} + // Add gap between horizontal candidates -- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; - rect->origin.x -= _seperatorWidth / 2; +void expandHighlightWidth(NSRect *rect, CGFloat extraSurrounding) { + if (!nearEmptyRect(*rect)) { + rect->size.width += extraSurrounding; + rect->origin.x -= extraSurrounding / 2; + } +} + +void removeCorner(NSMutableArray *highlightedPoints, NSMutableSet *rightCorners, NSRect containingRect) { + if (highlightedPoints && rightCorners) { + NSSet *originalRightCorners = [[NSSet alloc] initWithSet:rightCorners]; + for (NSNumber *cornerIndex in originalRightCorners) { + NSUInteger index = cornerIndex.unsignedIntegerValue; + NSPoint corner = [highlightedPoints[index] pointValue]; + CGFloat dist = MIN(NSMaxY(containingRect) - corner.y, corner.y - NSMinY(containingRect)); + if (dist < 1e-2) { + [rightCorners removeObject:cornerIndex]; + } } - } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; + } +} + +- (void) linearMultilineForRect:(NSRect)bodyRect leadingRect:(NSRect)leadingRect trailingRect:(NSRect)trailingRect points1:(NSMutableArray **)highlightedPoints points2:(NSMutableArray **)highlightedPoints2 rightCorners:(NSMutableSet **)rightCorners rightCorners2:(NSMutableSet **)rightCorners2 { + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + *highlightedPoints = [rectVertex(leadingRect) mutableCopy]; + *highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(2), @(3), nil]; + } else { + *highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { + if (NSMaxX(trailingRect) < NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), @(4), @(5), nil]; + } else if (NSMaxX(trailingRect) >= NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { + *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; + } } + } + if ([*highlightedPoints2 count] > 0) { + *rightCorners2 = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; + } +} + +- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect extraExpansion:(CGFloat)extraExpansion { + NSRect currentContainingRect = containingRect; + currentContainingRect.size.width += extraExpansion * 2; + currentContainingRect.size.height += extraExpansion * 2; + currentContainingRect.origin.x -= extraExpansion; + currentContainingRect.origin.y -= extraExpansion; + + CGFloat halfLinespace = theme.linespace / 2; + NSRect innerBox = backgroundRect; + innerBox.size.width -= (theme.edgeInset.width + 1) * 2 - 2 * extraExpansion; + innerBox.origin.x += theme.edgeInset.width + 1 - extraExpansion; + innerBox.size.height += 2 * extraExpansion; + innerBox.origin.y -= extraExpansion; + if (_preeditRange.length == 0) { + innerBox.origin.y += theme.edgeInset.height + 1; + innerBox.size.height -= (theme.edgeInset.height + 1) * 2; } else { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth; - rect->origin.x -= _seperatorWidth / 2; + innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; + innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + } + innerBox.size.height -= halfLinespace; + NSRect outerBox = backgroundRect; + outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; + outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; + outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; + outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; + + double effectiveRadius = MAX(0, theme.hilitedCornerRadius + 2 * extraExpansion / theme.hilitedCornerRadius * MAX(0, theme.cornerRadius - theme.hilitedCornerRadius)); + CGMutablePathRef path = CGPathCreateMutable(); + + if (theme.linear){ + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect extraSurounding:_seperatorWidth]; + + NSMutableArray *highlightedPoints; + NSMutableArray *highlightedPoints2; + NSMutableSet *rightCorners; + NSMutableSet *rightCorners2; + [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPoints points2:&highlightedPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; + + xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); + xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); + // Expand the boxes to reach proper border + enlarge(highlightedPoints, extraExpansion); + expand(highlightedPoints, innerBox, outerBox); + removeCorner(highlightedPoints, rightCorners, currentContainingRect); + + path = drawSmoothLines(highlightedPoints, rightCorners, 0.3*effectiveRadius, 1.4*effectiveRadius); + if (highlightedPoints2.count > 0) { + enlarge(highlightedPoints2, extraExpansion); + expand(highlightedPoints2, innerBox, outerBox); + removeCorner(highlightedPoints2, rightCorners2, currentContainingRect); + CGPathRef path2 = drawSmoothLines(highlightedPoints2, rightCorners2, 0.3*effectiveRadius, 1.4*effectiveRadius); + CGPathAddPath(path, NULL, path2); + } + } else { + NSRect highlightedRect = [self contentRectForRange:highlightedRange]; + highlightedRect.size.width = backgroundRect.size.width; + highlightedRect.size.height += theme.linespace; + highlightedRect.origin = NSMakePoint(backgroundRect.origin.x, highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); + if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + } + if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + if (_preeditRange.length == 0) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + } else { + highlightedRect.size.height += theme.hilitedCornerRadius / 2; + highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + } } + NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; + enlarge(highlightedPoints, extraExpansion); + expand(highlightedPoints, innerBox, outerBox); + path = drawSmoothLines(highlightedPoints, nil, 0.3*effectiveRadius, 1.4*effectiveRadius); } + return path; } // All draws happen here - (void)drawRect:(NSRect)dirtyRect { - NSBezierPath *backgroundPath; - NSBezierPath *borderPath; - NSBezierPath *highlightedPath; - NSBezierPath *highlightedPath2; - NSBezierPath *highlightedPreeditPath; - NSBezierPath *highlightedPreeditPath2; - NSBezierPath *preeditPath; + CGPathRef backgroundPath = CGPathCreateMutable(); + CGPathRef highlightedPath = CGPathCreateMutable(); + CGMutablePathRef candidatePaths = CGPathCreateMutable(); + CGMutablePathRef highlightedPreeditPath = CGPathCreateMutable(); + CGPathRef preeditPath = CGPathCreateMutable(); SquirrelTheme * theme = self.currentTheme; - NSRect textField = dirtyRect; - textField.origin.y += theme.edgeInset.height; - textField.origin.x += theme.edgeInset.width; + NSPoint textFieldOrigin = dirtyRect.origin; + textFieldOrigin.y += theme.edgeInset.height; + textFieldOrigin.x += theme.edgeInset.width; // Draw preedit Rect NSRect backgroundRect = dirtyRect; + NSRect containingRect = dirtyRect; + containingRect.size.height -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; + containingRect.size.width -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; + containingRect.origin.x += theme.hilitedCornerRadius + theme.borderWidth; + containingRect.origin.y += theme.hilitedCornerRadius + theme.borderWidth; // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = textField.size.width; + preeditRect.size.width = backgroundRect.size.width; preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); - if (_highlightedRange.length == 0) { + preeditRect.origin = backgroundRect.origin; + if (_candidateRanges.count == 0) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; } if (theme.preeditBackgroundColor != nil) { - preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); + preeditPath = drawSmoothLines(rectVertex(preeditRect), nil, 0, 0); } } // Draw highlighted Rect - if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; - } - NSRect outerBox = backgroundRect; - outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; - - CGFloat halfLinespace = theme.linespace / 2; - if (theme.linear){ - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - - [self addGapBetweenHorizontalCandidates:&leadingRect]; - [self addGapBetweenHorizontalCandidates:&bodyRect]; - [self addGapBetweenHorizontalCandidates:&trailingRect]; - - NSMutableArray *highlightedPoints; - NSMutableArray *highlightedPoints2; - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - } - - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - innerBox.size.height -= halfLinespace; - // Expand the boxes to reach proper border - expand(highlightedPoints, innerBox, outerBox); - expand(highlightedPoints2, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - if (highlightedPoints2.count > 0) { - highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + NSRange candidateRange = [_candidateRanges[i] rangeValue]; + if (i == _hilightedIndex) { + // Draw highlighted Rect + if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { + highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:0]; } } else { - NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; - highlightedRect.size.width = textField.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, - highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } + // Draw other highlighted Rect + if (candidateRange.length > 0 && theme.candidateBackColor != nil) { + CGPathRef candidatePath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:theme.surroundingExtraExpansion]; + CGPathAddPath(candidatePaths, NULL, candidatePath); } - NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - expand(highlightedPoints, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - NSRect innerBox = preeditRect; innerBox.size.width -= (theme.edgeInset.width + 1) * 2; innerBox.origin.x += theme.edgeInset.width + 1; innerBox.origin.y += theme.edgeInset.height + 1; - if (_highlightedRange.length == 0) { + if (_candidateRanges.count == 0) { innerBox.size.height -= (theme.edgeInset.height + 1) * 2; } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; + innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; } NSRect outerBox = preeditRect; - outerBox.size.height -= theme.hilitedCornerRadius; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2; - + outerBox.size.height -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; + outerBox.origin.y += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; + + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect extraSurounding:0]; + NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - } - // Expand the boxes to reach proper border + NSMutableSet *rightCorners; + NSMutableSet *rightCorners2; + [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPreeditPoints points2:&highlightedPreeditPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; + expand(highlightedPreeditPoints, innerBox, outerBox); - expand(highlightedPreeditPoints2, innerBox, outerBox); - highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + removeCorner(highlightedPreeditPoints, rightCorners, containingRect); + highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, rightCorners, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + expand(highlightedPreeditPoints2, innerBox, outerBox); + removeCorner(highlightedPreeditPoints2, rightCorners2, containingRect); + CGPathRef highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, rightCorners2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + CGPathAddPath(highlightedPreeditPath, NULL, highlightedPreeditPath2); } } [NSBezierPath setDefaultLineWidth:0]; - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); - _shape.path = backgroundPath.quartzPath; - // Nothing should extend beyond backgroundPath - borderPath = [backgroundPath copy]; - [borderPath addClip]; - borderPath.lineWidth = theme.borderWidth; - -// This block of code enables independent transparencies in highlighted colour and background colour. -// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. -#if 0 - // Calculate intersections. - if (![highlightedPath isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath copy]]; - if (![highlightedPath2 isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath2 copy]]; - } - } - - if (![preeditPath isEmpty]) { - [backgroundPath appendBezierPath:[preeditPath copy]]; - } - - if (![highlightedPreeditPath isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath copy]]; - } - if (![highlightedPreeditPath2 isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath2 copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath2 copy]]; + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), nil, theme.cornerRadius*0.3, theme.cornerRadius*1.4); + _shape.path = CGPathCreateMutableCopy(backgroundPath); + + [self.layer setSublayers: NULL]; + CGMutablePathRef backPath = CGPathCreateMutableCopy(backgroundPath); + if (!CGPathIsEmpty(preeditPath)) { + CGPathAddPath(backPath, NULL, preeditPath); + } + if (theme.mutualExclusive) { + if (!CGPathIsEmpty(highlightedPath)) { + CGPathAddPath(backPath, NULL, highlightedPath); + } + if (!CGPathIsEmpty(candidatePaths)) { + CGPathAddPath(backPath, NULL, candidatePaths); + } + } + CAShapeLayer *panelLayer = shapeFromPath(backPath); + panelLayer.fillColor = theme.backgroundColor.CGColor; + CAShapeLayer *panelLayerMask = shapeFromPath(backgroundPath); + panelLayer.mask = panelLayerMask; + [self.layer addSublayer: panelLayer]; + + if (theme.preeditBackgroundColor && !CGPathIsEmpty(preeditPath)) { + CAShapeLayer *layer = shapeFromPath(preeditPath); + layer.fillColor = theme.preeditBackgroundColor.CGColor; + CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); + if (theme.mutualExclusive && !CGPathIsEmpty(highlightedPreeditPath)) { + CGPathAddPath(maskPath, NULL, highlightedPreeditPath); + } + CAShapeLayer *mask = shapeFromPath(maskPath); + layer.mask = mask; + [panelLayer addSublayer: layer]; + } + if (theme.borderWidth > 0 && theme.borderColor) { + CAShapeLayer *borderLayer = shapeFromPath(backgroundPath); + borderLayer.lineWidth = theme.borderWidth * 2; + borderLayer.strokeColor = theme.borderColor.CGColor; + borderLayer.fillColor = NULL; + [panelLayer addSublayer: borderLayer]; + } + if (theme.highlightedPreeditColor && !CGPathIsEmpty(highlightedPreeditPath)) { + CAShapeLayer *layer = shapeFromPath(highlightedPreeditPath); + layer.fillColor = theme.highlightedPreeditColor.CGColor; + [panelLayer addSublayer: layer]; + } + if (theme.candidateBackColor && !CGPathIsEmpty(candidatePaths)) { + CAShapeLayer *layer = shapeFromPath(candidatePaths); + layer.fillColor = theme.candidateBackColor.CGColor; + [panelLayer addSublayer: layer]; + } + if (theme.highlightedBackColor && !CGPathIsEmpty(highlightedPath)) { + CAShapeLayer *layer = shapeFromPath(highlightedPath); + layer.fillColor = theme.highlightedBackColor.CGColor; + if (theme.shadowSize > 0) { + CAShapeLayer *shadowLayer = [CAShapeLayer layer]; + shadowLayer.shadowColor = NSColor.blackColor.CGColor; + shadowLayer.shadowOffset = NSMakeSize(theme.shadowSize/2, (theme.vertical ? -1 : 1) * theme.shadowSize/2); + shadowLayer.shadowPath = highlightedPath; + shadowLayer.shadowRadius = theme.shadowSize; + shadowLayer.shadowOpacity = 0.2; + CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); + CGPathAddPath(maskPath, NULL, highlightedPath); + if (!CGPathIsEmpty(preeditPath)) { + CGPathAddPath(maskPath, NULL, preeditPath); } + CAShapeLayer *shadowLayerMask = shapeFromPath(maskPath); + shadowLayer.mask = shadowLayerMask; + layer.strokeColor = [NSColor.blackColor colorWithAlphaComponent:0.15].CGColor; + layer.lineWidth = 0.5; + [layer addSublayer: shadowLayer]; } + [panelLayer addSublayer: layer]; } - [backgroundPath setWindingRule:NSEvenOddWindingRule]; - [preeditPath setWindingRule:NSEvenOddWindingRule]; -#endif - - [theme.backgroundColor setFill]; - [backgroundPath fill]; - if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { - [theme.preeditBackgroundColor setFill]; - [preeditPath fill]; - } - if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { - [theme.highlightedStripColor setFill]; - [highlightedPath fill]; - if (![highlightedPath2 isEmpty]) { - [highlightedPath2 fill]; - } - } - if (theme.highlightedPreeditColor && ![highlightedPreeditPath isEmpty]) { - [theme.highlightedPreeditColor setFill]; - [highlightedPreeditPath fill]; - if (![highlightedPreeditPath2 isEmpty]) { - [highlightedPreeditPath2 fill]; - } - } - - if (theme.borderColor && (theme.borderWidth > 0)) { - [theme.borderColor setStroke]; - [borderPath stroke]; - } - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + [_textView setTextContainerInset:NSMakeSize(textFieldOrigin.x, textFieldOrigin.y)]; } @end @@ -773,42 +861,6 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -CGFloat minimumHeight(NSDictionary *attribute) { - const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; - const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; - return minimumHeight; -} - -// Use this method to convert charcters to upright position -// Based on the width of the chacter, relative font size matters -void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { - NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; - double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; - // Use the width of the character to determin if they should be upright in vertical writing mode. - // Adjust font base line for better alignment. - const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; - const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; - const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; - const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; - stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; - NSUInteger i = stringRange.location; - while (i < stringRange.location+stringRange.length) { - NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; - i = range.location + range.length; - NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - // Also adjust the baseline so upright and lying charcters are properly aligned - if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { - [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; - NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - CGFloat widthDiff = charRect.size.width-cjkChar.size.width; - CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; - } else { - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; - } - } -} - void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -823,6 +875,20 @@ void fixDefaultFont(NSMutableAttributedString *text) { } } +NSAttributedString *insert(NSString *separator, NSAttributedString *betweenText) { + NSRange range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:0]; + NSAttributedString *attributedSeperator = [[NSAttributedString alloc] initWithString:separator attributes:[betweenText attributesAtIndex:0 effectiveRange:nil]]; + NSUInteger i = NSMaxRange(range); + NSMutableAttributedString *workingString = [[betweenText attributedSubstringFromRange:range] mutableCopy]; + while (i < betweenText.length) { + range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:i]; + [workingString appendAttributedString:attributedSeperator]; + [workingString appendAttributedString:[betweenText attributedSubstringFromRange:range]]; + i = NSMaxRange(range); + } + return workingString; +} + + (NSColor *)secondaryTextColor { if(@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -834,6 +900,7 @@ + (NSColor *)secondaryTextColor { - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; theme.native = YES; + theme.memorizeSize = YES; theme.candidateFormat = kDefaultCandidateFormat; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -867,8 +934,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -903,7 +970,8 @@ - (instancetype)init { [contentView addSubview:_back]; } [contentView addSubview:_view]; - + [contentView addSubview:_view.textView]; + self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; if (@available(macOS 10.14, *)) { @@ -929,6 +997,15 @@ - (void)getCurrentScreen { } } +- (CGFloat)getMaxTextWidth:(SquirrelTheme *)theme { + NSFont *currentFont = theme.attrs[NSFontAttributeName]; + CGFloat fontScale = currentFont.pointSize / 12; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + return theme.vertical + ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; +} + // Get the window size, the windows will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; @@ -942,28 +1019,20 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = _view.text.size.width; - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - CGFloat maxTextWidth = theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - if (textWidth > maxTextWidth) { - textWidth = maxTextWidth; - } - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); + CGFloat textWidth = [self getMaxTextWidth:theme]; + CGFloat maxTextHeight = theme.vertical ? _screenRect.size.width - theme.edgeInset.width * 2 : _screenRect.size.height - theme.edgeInset.height * 2; + _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || - (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { + if (theme.memorizeSize && ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect)))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); + _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); } } @@ -1011,14 +1080,17 @@ - (void)show { [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { - self.contentView.boundsRotation = -90.0; + self.contentView.boundsRotation = -90; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; } else { self.contentView.boundsRotation = 0; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; } BOOL translucency = theme.translucency; [_view setFrame:self.contentView.bounds]; + [_view.textView setFrame:self.contentView.bounds]; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; @@ -1069,6 +1141,8 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; + [self getCurrentScreen]; + CGFloat maxTextWidth = [self getMaxTextWidth:theme]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; @@ -1101,13 +1175,8 @@ - (void)showPreedit:(NSString *)preedit } [text appendAttributedString:line]; - NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; - if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, line.length)); - paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); - } [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit + value:theme.preeditParagraphStyle range:NSMakeRange(0, text.length)]; _preeditRange = NSMakeRange(0, text.length); @@ -1119,17 +1188,25 @@ - (void)showPreedit:(NSString *)preedit candidateStartPos = text.length; } - NSRange highlightedRange = NSMakeRange(NSNotFound, 0); + NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; // candidates NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; - NSDictionary *labelAttrs = - (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; - NSDictionary *commentAttrs = - (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; + NSDictionary *attrs; + NSDictionary *labelAttrs; + NSDictionary *commentAttrs; + if (i == index) { + attrs = theme.highlightedAttrs; + labelAttrs = theme.labelHighlightedAttrs; + commentAttrs = theme.commentHighlightedAttrs; + } else { + attrs = theme.attrs; + labelAttrs = theme.labelAttrs; + commentAttrs = theme.commentAttrs; + } + CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { @@ -1152,25 +1229,31 @@ - (void)showPreedit:(NSString *)preedit initWithString:labelString attributes:labelAttrs]]; // get the label size for indent - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(0, line.length)); - } if (!theme.linear) { - labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + NSMutableAttributedString *str = [line mutableCopy]; + if (theme.vertical) { + [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; + } + labelWidth = [str boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]]; + NSAttributedString *candidateAttributedString = [[NSAttributedString alloc] + initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]; + CGFloat candidateWidth = [candidateAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + if (candidateWidth <= maxTextWidth * 0.2) { + // Unicode Word Joiner + candidateAttributedString = insert(@"\u2060", candidateAttributedString); + } + + [line appendAttributedString:candidateAttributedString]; + // Use left-to-right marks to prevent right-to-left text from changing the // layout of non-candidate text. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); - } if (theme.suffixLabelFormat != nil) { NSString *labelString; @@ -1186,58 +1269,63 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; labelString = [NSString stringWithFormat:labelFormat, i+1]; } - NSUInteger suffixLabelStart = line.length; [line appendAttributedString: [[NSAttributedString alloc] initWithString:labelString attributes:labelAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); - } } if (i < comments.count && [comments[i] length] != 0) { - NSUInteger commentStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; + CGFloat candidateAndLabelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; NSString *comment = comments[i]; + NSAttributedString *commentAttributedString = [[NSAttributedString alloc] + initWithString:comment.precomposedStringWithCanonicalMapping + attributes:commentAttrs]; + CGFloat commentWidth = [commentAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + if (commentWidth <= maxTextWidth * 0.2) { + // Unicode Word Joiner + commentAttributedString = insert(@"\u2060", commentAttributedString); + } + + NSString *commentSeparator; + if (candidateAndLabelWidth + commentWidth <= maxTextWidth * 0.3) { + // Non-Breaking White Space + commentSeparator = @"\u00A0"; + } else { + commentSeparator = @" "; + } [line appendAttributedString:[[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping + initWithString:commentSeparator attributes:commentAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); - } + [line appendAttributedString:commentAttributedString]; } NSAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:attrs]; - _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; + + NSMutableAttributedString *str = [separator mutableCopy]; + if (theme.vertical) { + [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; + } + _view.seperatorWidth = [str boundingRectWithSize:NSZeroSize options:0].size.width; - NSMutableParagraphStyle *paragraphStyleCandidate; + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (i == 0) { - NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; - firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - paragraphStyleCandidate = firstParagraphStyle; + paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; } else { - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; [text appendAttributedString:separator]; } if (theme.linear) { paragraphStyleCandidate.lineSpacing = theme.linespace; } - if (theme.vertical) { - paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); - } paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; - if (i == index) { - highlightedRange = NSMakeRange(text.length, line.length); - } + NSRange candidateRange = NSMakeRange(text.length, line.length); + [candidateRanges addObject: [NSValue valueWithRange:candidateRange]]; [text appendAttributedString:line]; } @@ -1245,8 +1333,13 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(text); // text done! - [_view setText:text]; - [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view.textView.textStorage setAttributedString:text]; + if (theme.vertical) { + _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; + } else { + _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; + } + [_view drawViewWith:candidateRanges hilightedIndex:index preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; } @@ -1256,13 +1349,19 @@ - (void)updateStatus:(NSString *)message { - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.attrs]; + [text addAttribute:NSParagraphStyleAttributeName + value:theme.paragraphStyle + range:NSMakeRange(0, text.length)]; + fixDefaultFont(text); + [_view.textView.textStorage setAttributedString:text]; if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, text.length)); + _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; + } else { + _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; } - [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:[[NSArray alloc] init] hilightedIndex:0 preeditRange:emptyRange highlightedPreeditRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1361,22 +1460,30 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; + BOOL mutualExclusive = [config getBool:@"style/mutual_exclusive"]; + NSNumber *memorizeSizeConfig = [config getOptionalBool:@"style/memorize_size"]; + if (memorizeSizeConfig) { + theme.memorizeSize = memorizeSizeConfig.boolValue; + } + NSString *candidateFormat = [config getString:@"style/candidate_format"]; - NSString *fontName = [config getString:@"style/font_face"]; - NSInteger fontSize = [config getDouble:@"style/font_point"]; + CGFloat fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; + CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; - CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); + CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; + NSNumber *alphaValue = [config getOptionalDouble:@"style/alpha"]; + CGFloat alpha = alphaValue ? fmin(fmax(alphaValue.doubleValue, 0.0), 1.0) : 1.0; CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; + CGFloat surroundingExtraExpansion = [config getDouble:@"style/surrounding_extra_expansion"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; + CGFloat shadowSize = fmax(0,[config getDouble:@"style/shadow_size"]); NSColor *backgroundColor; NSColor *borderColor; @@ -1389,6 +1496,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSColor *candidateTextColor; NSColor *highlightedCandidateTextColor; NSColor *highlightedCandidateBackColor; + NSColor *candidateBackColor; NSColor *commentTextColor; NSColor *highlightedCommentTextColor; @@ -1423,6 +1531,16 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo // if not otherwise specified, candidate text is also rendered in this color. candidateTextColor = textColor; } + candidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/label_color"]]; + highlightedCandidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; + if (!highlightedCandidateLabelColor) { + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' + // are both valid + highlightedCandidateLabelColor = + [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; + } highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; if (highlightedCandidateTextColor == nil) { @@ -1433,6 +1551,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (highlightedCandidateBackColor == nil) { highlightedCandidateBackColor = highlightedBackColor; } + candidateBackColor = + [config getColor:[prefix stringByAppendingString:@"/candidate_back_color"]]; commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; highlightedCommentTextColor = @@ -1459,6 +1579,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } + NSNumber *mutualExclusiveOverridden = + [config getOptionalBool:[prefix stringByAppendingString:@"/mutual_exclusive"]]; + if (mutualExclusiveOverridden) { + mutualExclusive = mutualExclusiveOverridden.boolValue; + } NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { @@ -1495,22 +1620,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSizeOverridden) { commentFontSize = commentFontSizeOverridden.integerValue; } - NSColor *candidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_color"]]; - if (candidateLabelColorOverridden) { - candidateLabelColor = candidateLabelColorOverridden; - } - NSColor *highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (!highlightedCandidateLabelColorOverridden) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' - // are both valid - highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; - } - if (highlightedCandidateLabelColorOverridden) { - highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; - } NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { @@ -1526,6 +1635,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (hilitedCornerRadiusOverridden) { hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; } + NSNumber *surroundingExtraExpansionOverridden = + [config getOptionalDouble:[prefix stringByAppendingString:@"/surrounding_extra_expansion"]]; + if (surroundingExtraExpansionOverridden) { + surroundingExtraExpansion = surroundingExtraExpansionOverridden.doubleValue; + } NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { @@ -1551,6 +1665,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } + NSNumber *shadowSizeOverridden = + [config getOptionalDouble:[prefix stringByAppendingString:@"/shadow_size"]]; + if (shadowSizeOverridden) { + shadowSize = shadowSizeOverridden.doubleValue; + } } if (fontSize == 0) { // default size @@ -1662,8 +1781,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; - labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; + labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; labelHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateLabelColor; commentAttrs[NSForegroundColorAttributeName] = commentTextColor; commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; @@ -1671,8 +1790,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -1683,26 +1802,30 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle:preeditParagraphStyle]; [theme setBackgroundColor:backgroundColor - highlightedStripColor:highlightedCandidateBackColor + highlightedBackColor:highlightedCandidateBackColor + candidateBackColor:candidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; NSSize edgeInset; if (vertical) { - edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); + edgeInset = NSMakeSize(borderHeight + cornerRadius, borderWidth + cornerRadius); } else { - edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); + edgeInset = NSMakeSize(borderWidth + cornerRadius, borderHeight + cornerRadius); } [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius + srdExtraExpansion:surroundingExtraExpansion + shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing - alpha:(alpha == 0 ? 1.0 : alpha) + alpha:alpha translucency:translucency + mutualExclusive:mutualExclusive linear:linear vertical:vertical inlinePreedit:inlinePreedit From a60eeff8963c30710ef444f2b94ba4d2b09b15e0 Mon Sep 17 00:00:00 2001 From: LEO Yoon-Tsaw Date: Sun, 5 Feb 2023 15:43:27 -0500 Subject: [PATCH 016/164] Remove feature: (non-lighlighted) candidate background color --- SquirrelPanel.m | 110 ++++++------------------------------------------ 1 file changed, 12 insertions(+), 98 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 8ccda4ea0..ad91b4310 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -16,14 +16,12 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSColor *backgroundColor; @property(nonatomic, strong, readonly) NSColor *highlightedBackColor; -@property(nonatomic, strong, readonly) NSColor *candidateBackColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @property(nonatomic, strong, readonly) NSColor *borderColor; @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; -@property(nonatomic, readonly) CGFloat surroundingExtraExpansion; @property(nonatomic, readonly) CGFloat shadowSize; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat borderWidth; @@ -54,14 +52,12 @@ - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedBackColor:(NSColor *)highlightedBackColor - candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius - srdExtraExpansion:(CGFloat)surroundingExtraExpansion shadowSize:(CGFloat)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(CGFloat)borderWidth @@ -119,13 +115,11 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedBackColor:(NSColor *)highlightedBackColor - candidateBackColor:(NSColor *)candidateBackColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; _highlightedBackColor = highlightedBackColor; - _candidateBackColor = candidateBackColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; _borderColor = borderColor; @@ -133,7 +127,6 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(double)cornerRadius hilitedCornerRadius:(double)hilitedCornerRadius - srdExtraExpansion:(double)surroundingExtraExpansion shadowSize:(double)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(double)borderWidth @@ -148,7 +141,6 @@ - (void)setCornerRadius:(double)cornerRadius inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; - _surroundingExtraExpansion = surroundingExtraExpansion; _shadowSize = shadowSize; _edgeInset = edgeInset; _borderWidth = borderWidth; @@ -491,20 +483,6 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -CGPoint direction(CGPoint diff) { - if (diff.y == 0 && diff.x > 0) { - return NSMakePoint(0, 1); - } else if (diff.y == 0 && diff.x < 0) { - return NSMakePoint(0, -1); - } else if (diff.x == 0 && diff.y > 0) { - return NSMakePoint(-1, 0); - } else if (diff.x == 0 && diff.y < 0) { - return NSMakePoint(1, 0); - } else { - return NSMakePoint(0, 0); - } -} - CAShapeLayer *shapeFromPath(CGPathRef path) { CAShapeLayer *layer = [CAShapeLayer layer]; layer.path = path; @@ -512,31 +490,6 @@ CGPoint direction(CGPoint diff) { return layer; } -// Assumes clockwise iteration -void enlarge(NSMutableArray *vertex, CGFloat by) { - if (by != 0) { - NSPoint previousPoint; - NSPoint point; - NSPoint nextPoint; - NSArray *original = [[NSArray alloc] initWithArray:vertex]; - NSPoint newPoint; - NSPoint displacement; - for (NSUInteger i = 0; i < original.count; i += 1){ - previousPoint = [original[(original.count+i-1)%original.count] pointValue]; - point = [original[i] pointValue]; - nextPoint = [original[(i+1)%original.count] pointValue]; - newPoint = point; - displacement = direction(NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y)); - newPoint.x += by * displacement.x; - newPoint.y += by * displacement.y; - displacement = direction(NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y)); - newPoint.x += by * displacement.x; - newPoint.y += by * displacement.y; - [vertex replaceObjectAtIndex:i withObject:@(newPoint)]; - } - } -} - // Add gap between horizontal candidates void expandHighlightWidth(NSRect *rect, CGFloat extraSurrounding) { if (!nearEmptyRect(*rect)) { @@ -580,19 +533,13 @@ - (void) linearMultilineForRect:(NSRect)bodyRect leadingRect:(NSRect)leadingRect } } -- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect extraExpansion:(CGFloat)extraExpansion { +- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect { NSRect currentContainingRect = containingRect; - currentContainingRect.size.width += extraExpansion * 2; - currentContainingRect.size.height += extraExpansion * 2; - currentContainingRect.origin.x -= extraExpansion; - currentContainingRect.origin.y -= extraExpansion; CGFloat halfLinespace = theme.linespace / 2; NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2 - 2 * extraExpansion; - innerBox.origin.x += theme.edgeInset.width + 1 - extraExpansion; - innerBox.size.height += 2 * extraExpansion; - innerBox.origin.y -= extraExpansion; + innerBox.size.width -= (theme.edgeInset.width + 1) * 2; + innerBox.origin.x += theme.edgeInset.width + 1; if (_preeditRange.length == 0) { innerBox.origin.y += theme.edgeInset.height + 1; innerBox.size.height -= (theme.edgeInset.height + 1) * 2; @@ -602,12 +549,12 @@ - (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRang } innerBox.size.height -= halfLinespace; NSRect outerBox = backgroundRect; - outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; - outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; - outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; - outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; + outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); + outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; + outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - double effectiveRadius = MAX(0, theme.hilitedCornerRadius + 2 * extraExpansion / theme.hilitedCornerRadius * MAX(0, theme.cornerRadius - theme.hilitedCornerRadius)); + double effectiveRadius = MAX(0, theme.hilitedCornerRadius); CGMutablePathRef path = CGPathCreateMutable(); if (theme.linear){ @@ -625,13 +572,11 @@ - (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRang xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); // Expand the boxes to reach proper border - enlarge(highlightedPoints, extraExpansion); expand(highlightedPoints, innerBox, outerBox); removeCorner(highlightedPoints, rightCorners, currentContainingRect); path = drawSmoothLines(highlightedPoints, rightCorners, 0.3*effectiveRadius, 1.4*effectiveRadius); if (highlightedPoints2.count > 0) { - enlarge(highlightedPoints2, extraExpansion); expand(highlightedPoints2, innerBox, outerBox); removeCorner(highlightedPoints2, rightCorners2, currentContainingRect); CGPathRef path2 = drawSmoothLines(highlightedPoints2, rightCorners2, 0.3*effectiveRadius, 1.4*effectiveRadius); @@ -655,7 +600,6 @@ - (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRang } } NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - enlarge(highlightedPoints, extraExpansion); expand(highlightedPoints, innerBox, outerBox); path = drawSmoothLines(highlightedPoints, nil, 0.3*effectiveRadius, 1.4*effectiveRadius); } @@ -666,7 +610,6 @@ - (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRang - (void)drawRect:(NSRect)dirtyRect { CGPathRef backgroundPath = CGPathCreateMutable(); CGPathRef highlightedPath = CGPathCreateMutable(); - CGMutablePathRef candidatePaths = CGPathCreateMutable(); CGMutablePathRef highlightedPreeditPath = CGPathCreateMutable(); CGPathRef preeditPath = CGPathCreateMutable(); SquirrelTheme * theme = self.currentTheme; @@ -699,20 +642,10 @@ - (void)drawRect:(NSRect)dirtyRect { } // Draw highlighted Rect - for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { - NSRange candidateRange = [_candidateRanges[i] rangeValue]; - if (i == _hilightedIndex) { - // Draw highlighted Rect - if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { - highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:0]; - } - } else { - // Draw other highlighted Rect - if (candidateRange.length > 0 && theme.candidateBackColor != nil) { - CGPathRef candidatePath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:theme.surroundingExtraExpansion]; - CGPathAddPath(candidatePaths, NULL, candidatePath); - } - } + NSRange candidateRange = [_candidateRanges[_hilightedIndex] rangeValue]; + // Draw highlighted Rect + if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { + highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect]; } // Draw highlighted part of preedit text @@ -767,9 +700,6 @@ - (void)drawRect:(NSRect)dirtyRect { if (!CGPathIsEmpty(highlightedPath)) { CGPathAddPath(backPath, NULL, highlightedPath); } - if (!CGPathIsEmpty(candidatePaths)) { - CGPathAddPath(backPath, NULL, candidatePaths); - } } CAShapeLayer *panelLayer = shapeFromPath(backPath); panelLayer.fillColor = theme.backgroundColor.CGColor; @@ -800,11 +730,6 @@ - (void)drawRect:(NSRect)dirtyRect { layer.fillColor = theme.highlightedPreeditColor.CGColor; [panelLayer addSublayer: layer]; } - if (theme.candidateBackColor && !CGPathIsEmpty(candidatePaths)) { - CAShapeLayer *layer = shapeFromPath(candidatePaths); - layer.fillColor = theme.candidateBackColor.CGColor; - [panelLayer addSublayer: layer]; - } if (theme.highlightedBackColor && !CGPathIsEmpty(highlightedPath)) { CAShapeLayer *layer = shapeFromPath(highlightedPath); layer.fillColor = theme.highlightedBackColor.CGColor; @@ -1477,7 +1402,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat alpha = alphaValue ? fmin(fmax(alphaValue.doubleValue, 0.0), 1.0) : 1.0; CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; - CGFloat surroundingExtraExpansion = [config getDouble:@"style/surrounding_extra_expansion"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; @@ -1496,7 +1420,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSColor *candidateTextColor; NSColor *highlightedCandidateTextColor; NSColor *highlightedCandidateBackColor; - NSColor *candidateBackColor; NSColor *commentTextColor; NSColor *highlightedCommentTextColor; @@ -1551,8 +1474,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (highlightedCandidateBackColor == nil) { highlightedCandidateBackColor = highlightedBackColor; } - candidateBackColor = - [config getColor:[prefix stringByAppendingString:@"/candidate_back_color"]]; commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; highlightedCommentTextColor = @@ -1635,11 +1556,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (hilitedCornerRadiusOverridden) { hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; } - NSNumber *surroundingExtraExpansionOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/surrounding_extra_expansion"]]; - if (surroundingExtraExpansionOverridden) { - surroundingExtraExpansion = surroundingExtraExpansionOverridden.doubleValue; - } NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { @@ -1803,7 +1719,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo [theme setBackgroundColor:backgroundColor highlightedBackColor:highlightedCandidateBackColor - candidateBackColor:candidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; @@ -1817,7 +1732,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius - srdExtraExpansion:surroundingExtraExpansion shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) From c5bc7d44d187148fea41586edb36bd94c26a2ee2 Mon Sep 17 00:00:00 2001 From: LEO Yoon-Tsaw Date: Sun, 5 Feb 2023 16:04:30 -0500 Subject: [PATCH 017/164] Realign code --- SquirrelPanel.m | 56 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index ad91b4310..02ced7542 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -15,7 +15,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL memorizeSize; @property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSColor *highlightedBackColor; +@property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @property(nonatomic, strong, readonly) NSColor *borderColor; @@ -51,7 +51,7 @@ @interface SquirrelTheme : NSObject - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedBackColor:(NSColor *)highlightedBackColor + highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; @@ -71,14 +71,14 @@ - (void)setCornerRadius:(CGFloat)cornerRadius inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate; -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +- (void) setAttrs:(NSMutableDictionary *)attrs + labelAttrs:(NSMutableDictionary *)labelAttrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs +commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; @@ -114,12 +114,12 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedBackColor:(NSColor *)highlightedBackColor + highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; - _highlightedBackColor = highlightedBackColor; + _highlightedStripColor = highlightedStripColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; _borderColor = borderColor; @@ -155,17 +155,17 @@ - (void)setCornerRadius:(double)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +- (void) setAttrs:(NSMutableDictionary *)attrs + labelAttrs:(NSMutableDictionary *)labelAttrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs +commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { _attrs = attrs; - _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; + _highlightedAttrs = highlightedAttrs; _labelHighlightedAttrs = labelHighlightedAttrs; _commentAttrs = commentAttrs; _commentHighlightedAttrs = commentHighlightedAttrs; @@ -644,7 +644,7 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw highlighted Rect NSRange candidateRange = [_candidateRanges[_hilightedIndex] rangeValue]; // Draw highlighted Rect - if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { + if (candidateRange.length > 0 && theme.highlightedStripColor != nil) { highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect]; } @@ -730,9 +730,9 @@ - (void)drawRect:(NSRect)dirtyRect { layer.fillColor = theme.highlightedPreeditColor.CGColor; [panelLayer addSublayer: layer]; } - if (theme.highlightedBackColor && !CGPathIsEmpty(highlightedPath)) { + if (theme.highlightedStripColor && !CGPathIsEmpty(highlightedPath)) { CAShapeLayer *layer = shapeFromPath(highlightedPath); - layer.fillColor = theme.highlightedBackColor.CGColor; + layer.fillColor = theme.highlightedStripColor.CGColor; if (theme.shadowSize > 0) { CAShapeLayer *shadowLayer = [CAShapeLayer layer]; shadowLayer.shadowColor = NSColor.blackColor.CGColor; @@ -859,8 +859,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs + highlightedAttrs:highlightedAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -1697,8 +1697,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; - highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; + highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; labelHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateLabelColor; commentAttrs[NSForegroundColorAttributeName] = commentTextColor; commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; @@ -1706,8 +1706,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs + highlightedAttrs:highlightedAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -1718,7 +1718,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle:preeditParagraphStyle]; [theme setBackgroundColor:backgroundColor - highlightedBackColor:highlightedCandidateBackColor + highlightedStripColor:highlightedCandidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; From 29288e58a1351c87124c7a405ada98a0d5cb823a Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 6 Feb 2023 07:31:21 +0100 Subject: [PATCH 018/164] Merge branch 'master' into yue --- CHANGELOG.md | 22 + Info.plist | 4 +- Squirrel.xcodeproj/project.pbxproj | 8 +- SquirrelInputController.m | 27 +- SquirrelPanel.m | 1029 ++++++++++++---------------- action-install.sh | 7 +- input_source.m | 64 +- main.m | 9 +- 8 files changed, 548 insertions(+), 622 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc2569dd5..97292188d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ + +## 0.16.2 (2023-02-05) + +#### 須知 + + * 升級安裝後遇輸入法不可用,須手動重新添加 [#704](https://github.com/rime/squirrel/issues/704) + +#### 主要更新 + + * 更新 Rime 核心算法庫至 [1.8.5](https://github.com/rime/librime/releases/tag/1.8.5) + * 修復:橫向候選欄 Tab 鍵應當用作移動插入點 [rime/librime#609](https://github.com/rime/librime/issues/609) + * 修復:macOS Mojave 及以下版本單擊 Shift 等修飾鍵失效 [#715](https://github.com/rime/squirrel/issues/715) + * 修復:全新安裝只添加一個輸入法選項(簡體中文) [#714](https://github.com/rime/squirrel/issues/714) + + +#### Bug Fixes + +* modifier change event in older macOS ([5c2b7e64](https://github.com/rime/squirrel/commit/5c2b7e64980b7e6b7eb3a8b392163ce89d244f37)) +* install one input mode or keep previous ones ([3bc6c2c0](https://github.com/rime/squirrel/commit/3bc6c2c0edbb1adaa22e79da65c6f0116b164de7)) + + + ## 0.16.1 (2023-01-30) diff --git a/Info.plist b/Info.plist index 83473bdac..402d73ea1 100644 --- a/Info.plist +++ b/Info.plist @@ -2,8 +2,6 @@ - TISInputSourceID - im.rime.inputmethod.Squirrel CFBundleDevelopmentRegion English CFBundleExecutable @@ -96,7 +94,7 @@ Hans tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 6f6f82318..7c244d5f2 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -632,7 +632,7 @@ CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.16.1; + CURRENT_PROJECT_VERSION = 0.16.2; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", @@ -678,7 +678,7 @@ CLANG_ENABLE_OBJC_ARC = YES; CODE_SIGN_IDENTITY = "-"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 0.16.1; + CURRENT_PROJECT_VERSION = 0.16.2; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", @@ -760,7 +760,7 @@ /usr/local/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; @@ -809,7 +809,7 @@ /usr/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.9; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; }; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 004ba8a19..fbbf2286a 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -78,13 +78,22 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender handled = YES; break; } - int keyCode = event.keyCode; - //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx, keyCode: %d", sender, modifiers, keyCode); - int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); + //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + int rime_keycode = 0; + // For flags-changed event, keyCode is available since macOS 10.15 (#715) + Bool keyCodeAvailable = NO; + if (@available(macOS 10.15, *)) { + keyCodeAvailable = YES; + rime_keycode = osx_keycode_to_rime_keycode(event.keyCode, 0, 0, 0); + //NSLog(@"keyCode: %d", event.keyCode); + } int release_mask = 0; NSUInteger changes = _lastModifier ^ modifiers; if (changes & OSX_CAPITAL_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Caps_Lock; + } // NOTE: rime assumes XK_Caps_Lock to be sent before modifier changes, // while NSFlagsChanged event has the flag changed already. // so it is necessary to revert kLockMask. @@ -92,18 +101,30 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender [self processKey:rime_keycode modifiers:rime_modifiers]; } if (changes & OSX_SHIFT_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Shift_L; + } release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask; [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_CTRL_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Control_L; + } release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask; [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_ALT_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Alt_L; + } release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask; [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_COMMAND_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Super_L; + } release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask; [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; // do not update UI when using Command key diff --git a/SquirrelPanel.m b/SquirrelPanel.m index e93734587..f61b0b5ce 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,35 +3,76 @@ #import "SquirrelConfig.h" #import +@implementation NSBezierPath (BezierPathQuartzUtilities) +// This method works only in OS X v10.2 and later. +- (CGPathRef)quartzPath { + NSInteger i, numElements; + // Need to begin a path here. + CGPathRef immutablePath = NULL; + + // Then draw the path elements. + numElements = [self elementCount]; + if (numElements > 0) { + CGMutablePathRef path = CGPathCreateMutable(); + NSPoint points[3]; + BOOL didClosePath = YES; + for (i = 0; i < numElements; i++) { + switch ([self elementAtIndex:i associatedPoints:points]) { + case NSMoveToBezierPathElement: + CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); + break; + case NSLineToBezierPathElement: + CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); + didClosePath = NO; + break; + case NSCurveToBezierPathElement: + CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, + points[1].x, points[1].y, + points[2].x, points[2].y); + didClosePath = NO; + break; + case NSClosePathBezierPathElement: + CGPathCloseSubpath(path); + didClosePath = YES; + break; + } + } + + // Be sure the path is closed or Quartz may not do valid hit detection. + if (!didClosePath) { + CGPathCloseSubpath(path); + } + immutablePath = CGPathCreateCopy(path); + CGPathRelease(path); + } + return immutablePath; +} +@end + static const CGFloat kOffsetHeight = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c.\u00A0%@"; +static NSString *const kDefaultCandidateFormat = @"%c. %@"; @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL native; -@property(nonatomic, assign) BOOL memorizeSize; @property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSColor *highlightedBackColor; -@property(nonatomic, strong, readonly) NSColor *candidateBackColor; +@property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @property(nonatomic, strong, readonly) NSColor *borderColor; @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; -@property(nonatomic, readonly) CGFloat surroundingExtraExpansion; -@property(nonatomic, readonly) CGFloat shadowSize; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; -@property(nonatomic, readonly) BOOL mutualExclusive; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -53,36 +94,32 @@ @interface SquirrelTheme : NSObject - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedBackColor:(NSColor *)highlightedBackColor - candidateBackColor:(NSColor *)candidateBackColor + highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius - srdExtraExpansion:(CGFloat)surroundingExtraExpansion - shadowSize:(CGFloat)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(BOOL)translucency - mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate; -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +- (void) setAttrs:(NSMutableDictionary *)attrs + labelAttrs:(NSMutableDictionary *)labelAttrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs +commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; @@ -118,14 +155,12 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } - (void)setBackgroundColor:(NSColor *)backgroundColor - highlightedBackColor:(NSColor *)highlightedBackColor - candidateBackColor:(NSColor *)candidateBackColor + highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; - _highlightedBackColor = highlightedBackColor; - _candidateBackColor = candidateBackColor; + _highlightedStripColor = highlightedStripColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; _borderColor = borderColor; @@ -133,29 +168,23 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(double)cornerRadius hilitedCornerRadius:(double)hilitedCornerRadius - srdExtraExpansion:(double)surroundingExtraExpansion - shadowSize:(double)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(double)alpha + alpha:(CGFloat)alpha translucency:(BOOL)translucency - mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; - _surroundingExtraExpansion = surroundingExtraExpansion; - _shadowSize = shadowSize; _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; _alpha = alpha; _translucency = translucency; - _mutualExclusive = mutualExclusive; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -163,17 +192,17 @@ - (void)setCornerRadius:(double)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +- (void) setAttrs:(NSMutableDictionary *)attrs + labelAttrs:(NSMutableDictionary *)labelAttrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs +commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { _attrs = attrs; - _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; + _highlightedAttrs = highlightedAttrs; _labelHighlightedAttrs = labelHighlightedAttrs; _commentAttrs = commentAttrs; _commentHighlightedAttrs = commentHighlightedAttrs; @@ -191,9 +220,8 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextView *textView; -@property(nonatomic, readonly) NSArray *candidateRanges; -@property(nonatomic, readonly) NSInteger hilightedIndex; +@property(nonatomic, readonly) NSTextStorage *text; +@property(nonatomic, readonly) NSRange highlightedRange; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRect contentRect; @@ -202,10 +230,11 @@ @interface SquirrelView : NSView @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; -- (void) drawViewWith:(NSArray *)candidateRanges - hilightedIndex:(NSInteger)hilightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; +- (BOOL)isFlipped; +- (void)setText:(NSAttributedString *)text; +- (void)drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -242,60 +271,45 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } - _textView = [[NSTextView alloc] initWithFrame:frameRect]; + // Use textStorage to store text and manage all text layout and draws NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - [_textView replaceTextContainer:textContainer]; - _textView.layoutManager.backgroundLayoutEnabled = YES; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + [layoutManager addTextContainer:textContainer]; + _text = [[NSTextStorage alloc] init]; + [_text addLayoutManager:layoutManager]; + layoutManager.backgroundLayoutEnabled = YES; _defaultTheme = [[SquirrelTheme alloc] init]; + _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } - _shape = [[CAShapeLayer alloc] init]; return self; } // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; - __block long actualWidth = 0; - [_textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { - NSRange range = [self.textView.layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; - NSAttributedString *str = [self.textView.textStorage attributedSubstringFromRange:range]; - NSRange nonWhiteRange = [str.string rangeOfCharacterFromSet:NSCharacterSet.whitespaceAndNewlineCharacterSet.invertedSet options:NSBackwardsSearch]; - if (nonWhiteRange.location != NSNotFound) { - NSRange newRange = NSMakeRange(range.location, NSMaxRange(nonWhiteRange)); - NSRange newGlyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:newRange actualCharacterRange:NULL]; - CGFloat width = [self.textView.layoutManager boundingRectForGlyphRange:newGlyphRange inTextContainer:self.textView.textContainer].size.width; - if (width > actualWidth) { - actualWidth = width; - } - } - }]; - if (actualWidth > 0) { - rect.size.width = actualWidth; - } + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; return rect; } +- (void)setText:(NSAttributedString *)text { + [_text setAttributedString:[text copy]]; +} + // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWith:(NSArray *)candidateRanges - hilightedIndex:(NSInteger)hilightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { - _candidateRanges = candidateRanges; - _hilightedIndex = hilightedIndex; +- (void)drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange { + _highlightedRange = hilightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; self.needsDisplay = YES; @@ -307,15 +321,14 @@ - (void) drawViewWith:(NSArray *)candidateRanges return 1; } else if (number <= -2) { return -1; - } else { + }else { return number / 2; } } // Bezier cubic curve, which has continuous roundness -CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * __nullable straightCorner, CGFloat alpha, CGFloat beta) { - beta = MAX(0.00001, beta); - CGMutablePathRef path = CGPathCreateMutable(); +NSBezierPath *drawSmoothLines(NSArray *vertex, CGFloat alpha, CGFloat beta) { + NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) return path; NSPoint previousPoint = [vertex[vertex.count-1] pointValue]; @@ -325,39 +338,40 @@ CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * NSPoint control2; NSPoint target = previousPoint; NSPoint diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (!straightCorner || ![straightCorner containsObject:[NSNumber numberWithUnsignedInteger:vertex.count - 1]]) { + if (ABS(diff.x) >= ABS(diff.y)) { target.x += sign(diff.x/beta)*beta; + } else { target.y += sign(diff.y/beta)*beta; } - CGPathMoveToPoint(path, NULL, target.x, target.y); + [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; i += 1) { previousPoint = [vertex[(vertex.count+i-1)%vertex.count] pointValue]; point = [vertex[i] pointValue]; nextPoint = [vertex[(i+1)%vertex.count] pointValue]; target = point; - if (straightCorner && [straightCorner containsObject:[NSNumber numberWithUnsignedInteger:i]]) { - CGPathAddLineToPoint(path, NULL, target.x, target.y); - } else { - control1 = point; - diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); + control1 = point; + diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); + if (ABS(diff.x) >= ABS(diff.y)) { target.x -= sign(diff.x/beta)*beta; control1.x -= sign(diff.x/beta)*alpha; + } else { target.y -= sign(diff.y/beta)*beta; control1.y -= sign(diff.y/beta)*alpha; - - CGPathAddLineToPoint(path, NULL, target.x, target.y); - target = point; - control2 = point; - diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); + } + [path lineToPoint:target]; + target = point; + control2 = point; + diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); + if (ABS(diff.x) > ABS(diff.y)) { control2.x += sign(diff.x/beta)*alpha; target.x += sign(diff.x/beta)*beta; + } else { control2.y += sign(diff.y/beta)*alpha; target.y += sign(diff.y/beta)*beta; - - CGPathAddCurveToPoint(path, NULL, control1.x, control1.y, control2.x, control2.y, target.x, target.y); } + [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; } - CGPathCloseSubpath(path); + [path closePath]; return path; } @@ -386,33 +400,27 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _textView.layoutManager; - NSTextContainer *textContainer = _textView.textContainer; + NSLayoutManager *layoutManager = _text.layoutManagers[0]; + NSTextContainer *textContainer = layoutManager.textContainers[0]; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange firstLineRange = NSMakeRange(NSNotFound, 0); - NSRect firstLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&firstLineRange]; - NSRange lastLineRange = NSMakeRange(NSNotFound, 0); - NSRect lastLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&lastLineRange]; - + NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; *leadingRect = NSZeroRect; *bodyRect = boundingRect; *trailingRect = NSZeroRect; - // Multiline, not starting from beginning - if (boundingRect.origin.x <= 1 && firstLineRange.location < glyphRange.location) { - *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(firstLineRange.location, glyphRange.location-firstLineRange.location) inTextContainer:textContainer]; + if (boundingRect.origin.x <= 1 && fullRangeInBoundingRect.location < glyphRange.location) { + *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(fullRangeInBoundingRect.location, glyphRange.location-fullRangeInBoundingRect.location) inTextContainer:textContainer]; if (!nearEmptyRect(*leadingRect)) { bodyRect->size.height -= leadingRect->size.height; bodyRect->origin.y += leadingRect->size.height; } double rightEdge = NSMaxX(*leadingRect); leadingRect->origin.x = rightEdge; - leadingRect->size.width = NSMaxX(*bodyRect) - rightEdge; + leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; } - // Has trainling characters - if (NSMaxRange(lastLineRange) > NSMaxRange(glyphRange)) { + if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(NSMaxRange(glyphRange), NSMaxRange(lastLineRange)-NSMaxRange(glyphRange)) + NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; @@ -420,13 +428,29 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe double leftEdge = NSMinX(*trailingRect); trailingRect->origin.x = bodyRect->origin.x; trailingRect->size.width = leftEdge - bodyRect->origin.x; - // Has no trainling charcater + } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { + *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; + if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { + *trailingRect = NSZeroRect; + } else if (!nearEmptyRect(*trailingRect)) { + bodyRect->size.height -= trailingRect->size.height; + } } - - if ((!nearEmptyRect(*leadingRect) && bodyRect->size.height < leadingRect->size.height/2) || (!nearEmptyRect(*trailingRect) && bodyRect->size.height < trailingRect->size.height/2)) { - *bodyRect = NSZeroRect; + NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; + lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; + NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; + NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { + lastLineRange.length -= 1; + glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + } + if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { + if (!nearEmptyRect(*trailingRect)) { + *trailingRect = lastLineRect; + } else { + *bodyRect = lastLineRect; + } } - NSSize edgeInset = self.currentTheme.edgeInset; leadingRect->origin.x += edgeInset.width; leadingRect->origin.y += edgeInset.height; @@ -447,20 +471,20 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; + return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], bodyVertex[1]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { NSArray * trailingVertex = rectVertex(trailingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3], bodyVertex[0]]; + return @[bodyVertex[0], bodyVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; + return @[trailingVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], leadingVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1], bodyVertex[0]]; + return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else { return @[]; } @@ -484,60 +508,14 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -CGPoint direction(CGPoint diff) { - if (diff.y == 0 && diff.x > 0) { - return NSMakePoint(0, 1); - } else if (diff.y == 0 && diff.x < 0) { - return NSMakePoint(0, -1); - } else if (diff.x == 0 && diff.y > 0) { - return NSMakePoint(-1, 0); - } else if (diff.x == 0 && diff.y < 0) { - return NSMakePoint(1, 0); - } else { - return NSMakePoint(0, 0); - } -} - -CAShapeLayer *shapeFromPath(CGPathRef path) { - CAShapeLayer *layer = [CAShapeLayer layer]; - layer.path = path; - layer.fillRule = kCAFillRuleEvenOdd; - return layer; -} - -// Assumes clockwise iteration -void enlarge(NSMutableArray *vertex, CGFloat by) { - if (by != 0) { - NSPoint previousPoint; - NSPoint point; - NSPoint nextPoint; - NSArray *original = [[NSArray alloc] initWithArray:vertex]; - NSPoint newPoint; - NSPoint displacement; - for (NSUInteger i = 0; i < original.count; i += 1){ - previousPoint = [original[(original.count+i-1)%original.count] pointValue]; - point = [original[i] pointValue]; - nextPoint = [original[(i+1)%original.count] pointValue]; - newPoint = point; - displacement = direction(NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y)); - newPoint.x += by * displacement.x; - newPoint.y += by * displacement.y; - displacement = direction(NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y)); - newPoint.x += by * displacement.x; - newPoint.y += by * displacement.y; - [vertex replaceObjectAtIndex:i withObject:@(newPoint)]; - } - } -} - // Add gap between horizontal candidates -- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect range:(NSRange)highlightedRange { - if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { +- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { + if (_highlightedRange.location+_highlightedRange.length == _text.length) { if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth; + rect->size.width += _seperatorWidth / 2; rect->origin.x -= _seperatorWidth / 2; } - } else if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { if (!nearEmptyRect(*rect)) { rect->size.width += _seperatorWidth / 2; } @@ -549,292 +527,220 @@ - (void)addGapBetweenHorizontalCandidates:(NSRect *)rect range:(NSRange)highligh } } -void removeCorner(NSMutableArray *highlightedPoints, NSMutableSet *rightCorners, NSRect containingRect) { - if (highlightedPoints && rightCorners) { - NSSet *originalRightCorners = [[NSSet alloc] initWithSet:rightCorners]; - for (NSNumber *cornerIndex in originalRightCorners) { - NSUInteger index = cornerIndex.unsignedIntegerValue; - NSPoint corner = [highlightedPoints[index] pointValue]; - CGFloat dist = MIN(NSMaxY(containingRect) - corner.y, corner.y - NSMinY(containingRect)); - if (dist < 1e-2) { - [rightCorners removeObject:cornerIndex]; - } - } - } -} - -- (void) linearMultilineForRect:(NSRect)bodyRect leadingRect:(NSRect)leadingRect trailingRect:(NSRect)trailingRect points1:(NSMutableArray **)highlightedPoints points2:(NSMutableArray **)highlightedPoints2 rightCorners:(NSMutableSet **)rightCorners rightCorners2:(NSMutableSet **)rightCorners2 { - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - *highlightedPoints = [rectVertex(leadingRect) mutableCopy]; - *highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(2), @(3), nil]; - } else { - *highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { - if (NSMaxX(trailingRect) < NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), @(4), @(5), nil]; - } else if (NSMaxX(trailingRect) >= NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; - } - } - } - if ([*highlightedPoints2 count] > 0) { - *rightCorners2 = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; - } -} - -- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect extraExpansion:(CGFloat)extraExpansion { - NSRect currentContainingRect = containingRect; - currentContainingRect.size.width += extraExpansion * 2; - currentContainingRect.size.height += extraExpansion * 2; - currentContainingRect.origin.x -= extraExpansion; - currentContainingRect.origin.y -= extraExpansion; - - CGFloat halfLinespace = theme.linespace / 2; - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2 - 2 * extraExpansion; - innerBox.origin.x += theme.edgeInset.width + 1 - extraExpansion; - innerBox.size.height += 2 * extraExpansion; - innerBox.origin.y -= extraExpansion; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; - } - innerBox.size.height -= halfLinespace; - NSRect outerBox = backgroundRect; - outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; - outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth) - 2 * extraExpansion; - outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; - outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2 - extraExpansion; - - double effectiveRadius = MAX(0, theme.hilitedCornerRadius + 2 * extraExpansion / theme.hilitedCornerRadius * MAX(0, theme.cornerRadius - theme.hilitedCornerRadius)); - CGMutablePathRef path = CGPathCreateMutable(); - - if (theme.linear){ - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - - [self addGapBetweenHorizontalCandidates:&leadingRect range:highlightedRange]; - [self addGapBetweenHorizontalCandidates:&bodyRect range:highlightedRange]; - [self addGapBetweenHorizontalCandidates:&trailingRect range:highlightedRange]; - - NSMutableArray *highlightedPoints; - NSMutableArray *highlightedPoints2; - NSMutableSet *rightCorners; - NSMutableSet *rightCorners2; - [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPoints points2:&highlightedPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; - - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - // Expand the boxes to reach proper border - enlarge(highlightedPoints, extraExpansion); - expand(highlightedPoints, innerBox, outerBox); - removeCorner(highlightedPoints, rightCorners, currentContainingRect); - - path = drawSmoothLines(highlightedPoints, rightCorners, 0.3*effectiveRadius, 1.4*effectiveRadius); - if (highlightedPoints2.count > 0) { - enlarge(highlightedPoints2, extraExpansion); - expand(highlightedPoints2, innerBox, outerBox); - removeCorner(highlightedPoints2, rightCorners2, currentContainingRect); - CGPathRef path2 = drawSmoothLines(highlightedPoints2, rightCorners2, 0.3*effectiveRadius, 1.4*effectiveRadius); - CGPathAddPath(path, NULL, path2); - } - } else { - NSRect highlightedRect = [self contentRectForRange:highlightedRange]; - highlightedRect.size.width = backgroundRect.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(backgroundRect.origin.x, highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } - } - NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - enlarge(highlightedPoints, extraExpansion); - expand(highlightedPoints, innerBox, outerBox); - path = drawSmoothLines(highlightedPoints, nil, 0.3*effectiveRadius, 1.4*effectiveRadius); - } - return path; -} - // All draws happen here - (void)drawRect:(NSRect)dirtyRect { - CGPathRef backgroundPath = CGPathCreateMutable(); - CGPathRef highlightedPath = CGPathCreateMutable(); - CGMutablePathRef candidatePaths = CGPathCreateMutable(); - CGMutablePathRef highlightedPreeditPath = CGPathCreateMutable(); - CGPathRef preeditPath = CGPathCreateMutable(); + NSBezierPath *backgroundPath; + NSBezierPath *borderPath; + NSBezierPath *highlightedPath; + NSBezierPath *highlightedPath2; + NSBezierPath *highlightedPreeditPath; + NSBezierPath *highlightedPreeditPath2; + NSBezierPath *preeditPath; SquirrelTheme * theme = self.currentTheme; - NSPoint textFieldOrigin = dirtyRect.origin; - textFieldOrigin.y += theme.edgeInset.height; - textFieldOrigin.x += theme.edgeInset.width; + NSRect textField = dirtyRect; + textField.origin.y += theme.edgeInset.height; + textField.origin.x += theme.edgeInset.width; // Draw preedit Rect NSRect backgroundRect = dirtyRect; - NSRect containingRect = dirtyRect; - containingRect.size.height -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; - containingRect.size.width -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; - containingRect.origin.x += theme.hilitedCornerRadius + theme.borderWidth; - containingRect.origin.y += theme.hilitedCornerRadius + theme.borderWidth; // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = backgroundRect.size.width; + preeditRect.size.width = textField.size.width; preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = backgroundRect.origin; - if (_candidateRanges.count == 0) { + preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); + if (_highlightedRange.length == 0) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; } if (theme.preeditBackgroundColor != nil) { - preeditPath = drawSmoothLines(rectVertex(preeditRect), nil, 0, 0); + preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); } } // Draw highlighted Rect - for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { - NSRange candidateRange = [_candidateRanges[i] rangeValue]; - if (i == _hilightedIndex) { - // Draw highlighted Rect - if (candidateRange.length > 0 && theme.highlightedBackColor != nil) { - highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:0]; + if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { + NSRect innerBox = backgroundRect; + innerBox.size.width -= (theme.edgeInset.width + 1) * 2; + innerBox.origin.x += theme.edgeInset.width + 1; + if (_preeditRange.length == 0) { + innerBox.origin.y += theme.edgeInset.height + 1; + innerBox.size.height -= (theme.edgeInset.height + 1) * 2; + } else { + innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; + innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + } + NSRect outerBox = backgroundRect; + outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; + outerBox.size.width -= theme.hilitedCornerRadius; + outerBox.origin.x += theme.hilitedCornerRadius / 2; + outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; + + CGFloat halfLinespace = theme.linespace / 2; + if (theme.linear){ + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + + [self addGapBetweenHorizontalCandidates:&leadingRect]; + [self addGapBetweenHorizontalCandidates:&bodyRect]; + [self addGapBetweenHorizontalCandidates:&trailingRect]; + + NSMutableArray *highlightedPoints; + NSMutableArray *highlightedPoints2; + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + highlightedPoints = [rectVertex(leadingRect) mutableCopy]; + highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; + } else { + highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + } + + xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); + xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); + innerBox.size.height -= halfLinespace; + // Expand the boxes to reach proper border + expand(highlightedPoints, innerBox, outerBox); + expand(highlightedPoints2, innerBox, outerBox); + highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + if (highlightedPoints2.count > 0) { + highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } } else { - // Draw other highlighted Rect - if (candidateRange.length > 0 && theme.candidateBackColor != nil) { - CGPathRef candidatePath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect extraExpansion:theme.surroundingExtraExpansion]; - CGPathAddPath(candidatePaths, NULL, candidatePath); + NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; + highlightedRect.size.width = textField.size.width; + highlightedRect.size.height += theme.linespace; + highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, + highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); + if (_highlightedRange.location+_highlightedRange.length == _text.length) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + } + if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + if (_preeditRange.length == 0) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + } else { + highlightedRect.size.height += theme.hilitedCornerRadius / 2; + highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + } } + NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; + expand(highlightedPoints, innerBox, outerBox); + highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + NSRect innerBox = preeditRect; innerBox.size.width -= (theme.edgeInset.width + 1) * 2; innerBox.origin.x += theme.edgeInset.width + 1; innerBox.origin.y += theme.edgeInset.height + 1; - if (_candidateRanges.count == 0) { + if (_highlightedRange.length == 0) { innerBox.size.height -= (theme.edgeInset.height + 1) * 2; } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; } NSRect outerBox = preeditRect; - outerBox.size.height -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - outerBox.origin.y += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - + outerBox.size.height -= theme.hilitedCornerRadius; + outerBox.size.width -= theme.hilitedCornerRadius; + outerBox.origin.x += theme.hilitedCornerRadius / 2; + outerBox.origin.y += theme.hilitedCornerRadius / 2; + NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; - NSMutableSet *rightCorners; - NSMutableSet *rightCorners2; - [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPreeditPoints points2:&highlightedPreeditPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; - + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; + highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; + } else { + highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + } + // Expand the boxes to reach proper border expand(highlightedPreeditPoints, innerBox, outerBox); - removeCorner(highlightedPreeditPoints, rightCorners, containingRect); - highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, rightCorners, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + expand(highlightedPreeditPoints2, innerBox, outerBox); + highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - expand(highlightedPreeditPoints2, innerBox, outerBox); - removeCorner(highlightedPreeditPoints2, rightCorners2, containingRect); - CGPathRef highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, rightCorners2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - CGPathAddPath(highlightedPreeditPath, NULL, highlightedPreeditPath2); + highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } } [NSBezierPath setDefaultLineWidth:0]; - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), nil, theme.cornerRadius*0.3, theme.cornerRadius*1.4); - _shape.path = CGPathCreateMutableCopy(backgroundPath); - - [self.layer setSublayers: NULL]; - CGMutablePathRef backPath = CGPathCreateMutableCopy(backgroundPath); - if (!CGPathIsEmpty(preeditPath)) { - CGPathAddPath(backPath, NULL, preeditPath); - } - if (theme.mutualExclusive) { - if (!CGPathIsEmpty(highlightedPath)) { - CGPathAddPath(backPath, NULL, highlightedPath); - } - if (!CGPathIsEmpty(candidatePaths)) { - CGPathAddPath(backPath, NULL, candidatePaths); - } - } - CAShapeLayer *panelLayer = shapeFromPath(backPath); - panelLayer.fillColor = theme.backgroundColor.CGColor; - CAShapeLayer *panelLayerMask = shapeFromPath(backgroundPath); - panelLayer.mask = panelLayerMask; - [self.layer addSublayer: panelLayer]; - - if (theme.preeditBackgroundColor && !CGPathIsEmpty(preeditPath)) { - CAShapeLayer *layer = shapeFromPath(preeditPath); - layer.fillColor = theme.preeditBackgroundColor.CGColor; - CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); - if (theme.mutualExclusive && !CGPathIsEmpty(highlightedPreeditPath)) { - CGPathAddPath(maskPath, NULL, highlightedPreeditPath); - } - CAShapeLayer *mask = shapeFromPath(maskPath); - layer.mask = mask; - [panelLayer addSublayer: layer]; - } - if (theme.borderWidth > 0 && theme.borderColor) { - CAShapeLayer *borderLayer = shapeFromPath(backgroundPath); - borderLayer.lineWidth = theme.borderWidth * 2; - borderLayer.strokeColor = theme.borderColor.CGColor; - borderLayer.fillColor = NULL; - [panelLayer addSublayer: borderLayer]; - } - if (theme.highlightedPreeditColor && !CGPathIsEmpty(highlightedPreeditPath)) { - CAShapeLayer *layer = shapeFromPath(highlightedPreeditPath); - layer.fillColor = theme.highlightedPreeditColor.CGColor; - [panelLayer addSublayer: layer]; - } - if (theme.candidateBackColor && !CGPathIsEmpty(candidatePaths)) { - CAShapeLayer *layer = shapeFromPath(candidatePaths); - layer.fillColor = theme.candidateBackColor.CGColor; - [panelLayer addSublayer: layer]; - } - if (theme.highlightedBackColor && !CGPathIsEmpty(highlightedPath)) { - CAShapeLayer *layer = shapeFromPath(highlightedPath); - layer.fillColor = theme.highlightedBackColor.CGColor; - if (theme.shadowSize > 0) { - CAShapeLayer *shadowLayer = [CAShapeLayer layer]; - shadowLayer.shadowColor = NSColor.blackColor.CGColor; - shadowLayer.shadowOffset = NSMakeSize(theme.shadowSize/2, (theme.vertical ? -1 : 1) * theme.shadowSize/2); - shadowLayer.shadowPath = highlightedPath; - shadowLayer.shadowRadius = theme.shadowSize; - shadowLayer.shadowOpacity = 0.2; - CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); - CGPathAddPath(maskPath, NULL, highlightedPath); - if (!CGPathIsEmpty(preeditPath)) { - CGPathAddPath(maskPath, NULL, preeditPath); + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); + _shape.path = backgroundPath.quartzPath; + // Nothing should extend beyond backgroundPath + borderPath = [backgroundPath copy]; + [borderPath addClip]; + borderPath.lineWidth = theme.borderWidth; + +// This block of code enables independent transparencies in highlighted colour and background colour. +// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. +#if 0 + // Calculate intersections. + if (![highlightedPath isEmpty]) { + [backgroundPath appendBezierPath:[highlightedPath copy]]; + if (![highlightedPath2 isEmpty]) { + [backgroundPath appendBezierPath:[highlightedPath2 copy]]; + } + } + + if (![preeditPath isEmpty]) { + [backgroundPath appendBezierPath:[preeditPath copy]]; + } + + if (![highlightedPreeditPath isEmpty]) { + if (preeditPath != nil) { + [preeditPath appendBezierPath:[highlightedPreeditPath copy]]; + } else { + [backgroundPath appendBezierPath:[highlightedPreeditPath copy]]; + } + if (![highlightedPreeditPath2 isEmpty]) { + if (preeditPath != nil) { + [preeditPath appendBezierPath:[highlightedPreeditPath2 copy]]; + } else { + [backgroundPath appendBezierPath:[highlightedPreeditPath2 copy]]; } - CAShapeLayer *shadowLayerMask = shapeFromPath(maskPath); - shadowLayer.mask = shadowLayerMask; - [layer addSublayer: shadowLayer]; } - [panelLayer addSublayer: layer]; } - [_textView setTextContainerInset:NSMakeSize(textFieldOrigin.x, textFieldOrigin.y)]; + [backgroundPath setWindingRule:NSEvenOddWindingRule]; + [preeditPath setWindingRule:NSEvenOddWindingRule]; +#endif + + [theme.backgroundColor setFill]; + [backgroundPath fill]; + if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { + [theme.preeditBackgroundColor setFill]; + [preeditPath fill]; + } + if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { + [theme.highlightedStripColor setFill]; + [highlightedPath fill]; + if (![highlightedPath2 isEmpty]) { + [highlightedPath2 fill]; + } + } + if (theme.highlightedPreeditColor && ![highlightedPreeditPath isEmpty]) { + [theme.highlightedPreeditColor setFill]; + [highlightedPreeditPath fill]; + if (![highlightedPreeditPath2 isEmpty]) { + [highlightedPreeditPath2 fill]; + } + } + + if (theme.borderColor && (theme.borderWidth > 0)) { + [theme.borderColor setStroke]; + [borderPath stroke]; + } + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; + [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; } @end @@ -867,6 +773,42 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } +CGFloat minimumHeight(NSDictionary *attribute) { + const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; + const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; + return minimumHeight; +} + +// Use this method to convert charcters to upright position +// Based on the width of the chacter, relative font size matters +void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { + NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; + double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; + // Use the width of the character to determin if they should be upright in vertical writing mode. + // Adjust font base line for better alignment. + const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; + const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; + const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; + const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; + stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; + NSUInteger i = stringRange.location; + while (i < stringRange.location+stringRange.length) { + NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; + i = range.location + range.length; + NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + // Also adjust the baseline so upright and lying charcters are properly aligned + if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { + [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; + NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + CGFloat widthDiff = charRect.size.width-cjkChar.size.width; + CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; + [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; + } else { + [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; + } + } +} + void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -881,20 +823,6 @@ void fixDefaultFont(NSMutableAttributedString *text) { } } -NSAttributedString *insert(NSString *separator, NSAttributedString *betweenText) { - NSRange range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:0]; - NSAttributedString *attributedSeperator = [[NSAttributedString alloc] initWithString:separator attributes:[betweenText attributesAtIndex:0 effectiveRange:nil]]; - NSUInteger i = NSMaxRange(range); - NSMutableAttributedString *workingString = [[betweenText attributedSubstringFromRange:range] mutableCopy]; - while (i < betweenText.length) { - range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:i]; - [workingString appendAttributedString:attributedSeperator]; - [workingString appendAttributedString:[betweenText attributedSubstringFromRange:range]]; - i = NSMaxRange(range); - } - return workingString; -} - + (NSColor *)secondaryTextColor { if(@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -906,7 +834,6 @@ + (NSColor *)secondaryTextColor { - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; theme.native = YES; - theme.memorizeSize = YES; theme.candidateFormat = kDefaultCandidateFormat; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -940,8 +867,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs + highlightedAttrs:highlightedAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -976,8 +903,7 @@ - (instancetype)init { [contentView addSubview:_back]; } [contentView addSubview:_view]; - [contentView addSubview:_view.textView]; - + self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; if (@available(macOS 10.14, *)) { @@ -1003,15 +929,6 @@ - (void)getCurrentScreen { } } -- (CGFloat)getMaxTextWidth:(SquirrelTheme *)theme { - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - return theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; -} - // Get the window size, the windows will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; @@ -1025,20 +942,28 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = [self getMaxTextWidth:theme]; - CGFloat maxTextHeight = theme.vertical ? _screenRect.size.width - theme.edgeInset.width * 2 : _screenRect.size.height - theme.edgeInset.height * 2; - _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); + CGFloat textWidth = _view.text.size.width; + NSFont *currentFont = theme.attrs[NSFontAttributeName]; + CGFloat fontScale = currentFont.pointSize / 12; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + CGFloat maxTextWidth = theme.vertical + ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; + if (textWidth > maxTextWidth) { + textWidth = maxTextWidth; + } + _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if (theme.memorizeSize && ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || - (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect)))) { + if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); + _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); } } @@ -1086,17 +1011,14 @@ - (void)show { [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { - self.contentView.boundsRotation = -90; - _view.textView.boundsRotation = 0; + self.contentView.boundsRotation = -90.0; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; } else { self.contentView.boundsRotation = 0; - _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; } BOOL translucency = theme.translucency; [_view setFrame:self.contentView.bounds]; - [_view.textView setFrame:self.contentView.bounds]; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; @@ -1147,8 +1069,6 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; - [self getCurrentScreen]; - CGFloat maxTextWidth = [self getMaxTextWidth:theme]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; @@ -1181,8 +1101,13 @@ - (void)showPreedit:(NSString *)preedit } [text appendAttributedString:line]; + NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; + if (theme.vertical) { + convertToVerticalGlyph(text, NSMakeRange(0, line.length)); + paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); + } [text addAttribute:NSParagraphStyleAttributeName - value:theme.preeditParagraphStyle + value:paragraphStylePreedit range:NSMakeRange(0, text.length)]; _preeditRange = NSMakeRange(0, text.length); @@ -1194,25 +1119,17 @@ - (void)showPreedit:(NSString *)preedit candidateStartPos = text.length; } - NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; + NSRange highlightedRange = NSMakeRange(NSNotFound, 0); // candidates NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs; - NSDictionary *labelAttrs; - NSDictionary *commentAttrs; - if (i == index) { - attrs = theme.highlightedAttrs; - labelAttrs = theme.labelHighlightedAttrs; - commentAttrs = theme.commentHighlightedAttrs; - } else { - attrs = theme.attrs; - labelAttrs = theme.labelAttrs; - commentAttrs = theme.commentAttrs; - } - + NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; + NSDictionary *labelAttrs = + (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; + NSDictionary *commentAttrs = + (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { @@ -1235,31 +1152,25 @@ - (void)showPreedit:(NSString *)preedit initWithString:labelString attributes:labelAttrs]]; // get the label size for indent + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(0, line.length)); + } if (!theme.linear) { - NSMutableAttributedString *str = [line mutableCopy]; - if (theme.vertical) { - [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; - } - labelWidth = [str boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - NSAttributedString *candidateAttributedString = [[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]; - CGFloat candidateWidth = [candidateAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; - if (candidateWidth <= maxTextWidth * 0.2) { - // Unicode Word Joiner - candidateAttributedString = insert(@"\u2060", candidateAttributedString); - } - - [line appendAttributedString:candidateAttributedString]; - + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]]; // Use left-to-right marks to prevent right-to-left text from changing the // layout of non-candidate text. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); + } if (theme.suffixLabelFormat != nil) { NSString *labelString; @@ -1275,63 +1186,58 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; labelString = [NSString stringWithFormat:labelFormat, i+1]; } + NSUInteger suffixLabelStart = line.length; [line appendAttributedString: [[NSAttributedString alloc] initWithString:labelString attributes:labelAttrs]]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); + } } if (i < comments.count && [comments[i] length] != 0) { - CGFloat candidateAndLabelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + NSUInteger commentStart = line.length; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:@" " + attributes:commentAttrs]]; NSString *comment = comments[i]; - NSAttributedString *commentAttributedString = [[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]; - CGFloat commentWidth = [commentAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; - if (commentWidth <= maxTextWidth * 0.2) { - // Unicode Word Joiner - commentAttributedString = insert(@"\u2060", commentAttributedString); - } - - NSString *commentSeparator; - if (candidateAndLabelWidth + commentWidth <= maxTextWidth * 0.3) { - // Non-Breaking White Space - commentSeparator = @"\u00A0"; - } else { - commentSeparator = @" "; - } [line appendAttributedString:[[NSAttributedString alloc] - initWithString:commentSeparator + initWithString:comment.precomposedStringWithCanonicalMapping attributes:commentAttrs]]; - [line appendAttributedString:commentAttributedString]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); + } } NSAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:attrs]; - - NSMutableAttributedString *str = [separator mutableCopy]; - if (theme.vertical) { - [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; - } - _view.seperatorWidth = [str boundingRectWithSize:NSZeroSize options:0].size.width; + _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; - NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + NSMutableParagraphStyle *paragraphStyleCandidate; if (i == 0) { - paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; + NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; + firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; + paragraphStyleCandidate = firstParagraphStyle; } else { + paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; [text appendAttributedString:separator]; } if (theme.linear) { paragraphStyleCandidate.lineSpacing = theme.linespace; } + if (theme.vertical) { + paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); + } paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; - NSRange candidateRange = NSMakeRange(text.length, line.length); - [candidateRanges addObject: [NSValue valueWithRange:candidateRange]]; + if (i == index) { + highlightedRange = NSMakeRange(text.length, line.length); + } [text appendAttributedString:line]; } @@ -1339,13 +1245,8 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(text); // text done! - [_view.textView.textStorage setAttributedString:text]; - if (theme.vertical) { - _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; - } else { - _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; - } - [_view drawViewWith:candidateRanges hilightedIndex:index preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view setText:text]; + [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; } @@ -1355,19 +1256,13 @@ - (void)updateStatus:(NSString *)message { - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.attrs]; - [text addAttribute:NSParagraphStyleAttributeName - value:theme.paragraphStyle - range:NSMakeRange(0, text.length)]; - fixDefaultFont(text); - [_view.textView.textStorage setAttributedString:text]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; if (theme.vertical) { - _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; - } else { - _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; + convertToVerticalGlyph(text, NSMakeRange(0, text.length)); } + [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:[[NSArray alloc] init] hilightedIndex:0 preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1466,30 +1361,22 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; - BOOL mutualExclusive = [config getBool:@"style/mutual_exclusive"]; - NSNumber *memorizeSizeConfig = [config getOptionalBool:@"style/memorize_size"]; - if (memorizeSizeConfig) { - theme.memorizeSize = memorizeSizeConfig.boolValue; - } - NSString *candidateFormat = [config getString:@"style/candidate_format"]; + NSString *fontName = [config getString:@"style/font_face"]; - CGFloat fontSize = [config getDouble:@"style/font_point"]; + NSInteger fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; + NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; - NSNumber *alphaValue = [config getOptionalDouble:@"style/alpha"]; - CGFloat alpha = alphaValue ? fmin(fmax(alphaValue.doubleValue, 0.0), 1.0) : 1.0; + NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; + CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; - CGFloat surroundingExtraExpansion = [config getDouble:@"style/surrounding_extra_expansion"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; - CGFloat shadowSize = fmax(0,[config getDouble:@"style/shadow_size"]); NSColor *backgroundColor; NSColor *borderColor; @@ -1502,7 +1389,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSColor *candidateTextColor; NSColor *highlightedCandidateTextColor; NSColor *highlightedCandidateBackColor; - NSColor *candidateBackColor; NSColor *commentTextColor; NSColor *highlightedCommentTextColor; @@ -1537,16 +1423,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo // if not otherwise specified, candidate text is also rendered in this color. candidateTextColor = textColor; } - candidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/label_color"]]; - highlightedCandidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (!highlightedCandidateLabelColor) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' - // are both valid - highlightedCandidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; - } highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; if (highlightedCandidateTextColor == nil) { @@ -1557,8 +1433,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (highlightedCandidateBackColor == nil) { highlightedCandidateBackColor = highlightedBackColor; } - candidateBackColor = - [config getColor:[prefix stringByAppendingString:@"/candidate_back_color"]]; commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; highlightedCommentTextColor = @@ -1585,11 +1459,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } - NSNumber *mutualExclusiveOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/mutual_exclusive"]]; - if (mutualExclusiveOverridden) { - mutualExclusive = mutualExclusiveOverridden.boolValue; - } NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { @@ -1626,6 +1495,22 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSizeOverridden) { commentFontSize = commentFontSizeOverridden.integerValue; } + NSColor *candidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/label_color"]]; + if (candidateLabelColorOverridden) { + candidateLabelColor = candidateLabelColorOverridden; + } + NSColor *highlightedCandidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; + if (!highlightedCandidateLabelColorOverridden) { + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' + // are both valid + highlightedCandidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; + } + if (highlightedCandidateLabelColorOverridden) { + highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; + } NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { @@ -1641,11 +1526,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (hilitedCornerRadiusOverridden) { hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; } - NSNumber *surroundingExtraExpansionOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/surrounding_extra_expansion"]]; - if (surroundingExtraExpansionOverridden) { - surroundingExtraExpansion = surroundingExtraExpansionOverridden.doubleValue; - } NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { @@ -1671,11 +1551,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } - NSNumber *shadowSizeOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/shadow_size"]]; - if (shadowSizeOverridden) { - shadowSize = shadowSizeOverridden.doubleValue; - } } if (fontSize == 0) { // default size @@ -1787,8 +1662,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; - highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; + highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; labelHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateLabelColor; commentAttrs[NSForegroundColorAttributeName] = commentTextColor; commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; @@ -1796,8 +1671,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs + highlightedAttrs:highlightedAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs @@ -1808,30 +1683,26 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle:preeditParagraphStyle]; [theme setBackgroundColor:backgroundColor - highlightedBackColor:highlightedCandidateBackColor - candidateBackColor:candidateBackColor + highlightedStripColor:highlightedCandidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; NSSize edgeInset; if (vertical) { - edgeInset = NSMakeSize(borderHeight + cornerRadius, borderWidth + cornerRadius); + edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); } else { - edgeInset = NSMakeSize(borderWidth + cornerRadius, borderHeight + cornerRadius); + edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); } [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius - srdExtraExpansion:surroundingExtraExpansion - shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing - alpha:alpha + alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency - mutualExclusive:mutualExclusive linear:linear vertical:vertical inlinePreedit:inlinePreedit diff --git a/action-install.sh b/action-install.sh index 72358c935..8f17d2e4e 100755 --- a/action-install.sh +++ b/action-install.sh @@ -2,12 +2,13 @@ set -e -rime_version=1.8.4 +rime_version=1.8.5 +rime_git_hash=08dd95f -rime_archive='rime-a94739f-macOS.tar.bz2' +rime_archive="rime-${rime_git_hash}-macOS.tar.bz2" rime_download_url="https://github.com/rime/librime/releases/download/${rime_version}/${rime_archive}" -rime_deps_archive='rime-deps-a94739f-macOS.tar.bz2' +rime_deps_archive="rime-deps-${rime_git_hash}-macOS.tar.bz2" rime_deps_download_url="https://github.com/rime/librime/releases/download/${rime_version}/${rime_deps_archive}" mkdir -p download && ( diff --git a/input_source.m b/input_source.m index 85b7e9739..4d1ad8742 100644 --- a/input_source.m +++ b/input_source.m @@ -2,15 +2,18 @@ static const unsigned char kInstallLocation[] = "/Library/Input Methods/Squirrel.app"; -static NSString *const kSourceID = - @"im.rime.inputmethod.Squirrel"; -static NSString *const kInputModeHantID = - @"im.rime.inputmethod.Squirrel.Hant"; -static NSString *const kInputModeHansID = + +static NSString *const kHansInputModeID = @"im.rime.inputmethod.Squirrel.Hans"; -static NSString *const kInputModeCantID = +static NSString *const kHantInputModeID = + @"im.rime.inputmethod.Squirrel.Hant"; +static NSString *const kCantInputModeID = @"im.rime.inputmethod.Squirrel.Cant"; +#define HANS_INPUT_MODE (1 << 0) +#define HANT_INPUT_MODE (1 << 1) +#define CANT_INPUT_MODE (1 << 2) + void RegisterInputSource() { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); @@ -21,18 +24,20 @@ void RegisterInputSource() { } } -void ActivateInputSource() { +void ActivateInputSource(int enabled_modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( sourceList, i)); NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); - //NSLog(@"examining input source '%@", sourceID); - if ([sourceID isEqualToString:kInputModeHantID] || - [sourceID isEqualToString:kInputModeHansID] || - [sourceID isEqualToString:kInputModeCantID] || - [sourceID isEqualToString:kSourceID]) { + //NSLog(@"Examining input source: %@", sourceID); + if ([sourceID isEqualToString:kHansInputModeID] && + ((enabled_modes & HANS_INPUT_MODE) != 0) || + [sourceID isEqualToString:kHantInputModeID] && + ((enabled_modes & HANT_INPUT_MODE) != 0) || + [sourceID isEqualToString:kCantInputModeID] && + ((enabled_modes & CANT_INPUT_MODE) != 0)) { TISEnableInputSource(inputSource); NSLog(@"Enabled input source: %@", sourceID); CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( @@ -54,19 +59,22 @@ void DeactivateInputSource() { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kInputModeHantID] || - [sourceID isEqualToString:kInputModeHansID] || - [sourceID isEqualToString:kInputModeCantID] || - [sourceID isEqualToString:kSourceID]) { - TISDisableInputSource(inputSource); - NSLog(@"Disabled input source: %@", sourceID); + if ([sourceID isEqualToString:kHansInputModeID] || + [sourceID isEqualToString:kHantInputModeID] || + [sourceID isEqualToString:kCantInputModeID]) { + CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( + inputSource, kTISPropertyInputSourceIsEnabled)); + if (CFBooleanGetValue(isEnabled)) { + TISDisableInputSource(inputSource); + NSLog(@"Disabled input source: %@", sourceID); + } } } CFRelease(sourceList); } -BOOL IsInputSourceActive() { - int active = 0; +int GetEnabledInputModes() { + int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( @@ -74,18 +82,20 @@ BOOL IsInputSourceActive() { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kInputModeHantID] || - [sourceID isEqualToString:kInputModeHansID] || - [sourceID isEqualToString:kInputModeCantID] || - [sourceID isEqualToString:kSourceID]) { + if ([sourceID isEqualToString:kHansInputModeID] || + [sourceID isEqualToString:kHantInputModeID] || + [sourceID isEqualToString:kCantInputModeID]) { CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceIsEnabled)); if (CFBooleanGetValue(isEnabled)) { - ++active; + if ([sourceID isEqualToString:kHansInputModeID]) + input_modes |= HANS_INPUT_MODE; + else if ([sourceID isEqualToString:kHantInputModeID]) + input_modes |= HANT_INPUT_MODE; } } } CFRelease(sourceList); - //NSLog(@"IsInputSourceActive: %d", active); - return active >= 2; // 1 active input method + 1 active input mode + NSLog(@"EnabledInputModes: %d", input_modes); + return input_modes; } diff --git a/main.m b/main.m index a98f1c76d..f4f95d6f7 100644 --- a/main.m +++ b/main.m @@ -6,9 +6,11 @@ #import void RegisterInputSource(); -void ActivateInputSource(); +int GetEnabledInputModes(); void DeactivateInputSource(); -// BOOL IsInputSourceActive(); +void ActivateInputSource(int input_modes); + +#define DEFAULT_INPUT_MODE 1 // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. @@ -35,8 +37,9 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--install", argv[1])) { // register and enable Squirrel RegisterInputSource(); + int input_modes = GetEnabledInputModes(); DeactivateInputSource(); - ActivateInputSource(); + ActivateInputSource(input_modes ? input_modes : DEFAULT_INPUT_MODE); return 0; } From 36b78bc031f1185427cd6a5db996f8fe0f34b380 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 6 Feb 2023 07:46:37 +0100 Subject: [PATCH 019/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 6a94998a3..cae3f639a 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 6a94998a3f933fad4fb06977d2d2723b7f2ffd17 +Subproject commit cae3f639a696eccee8017d2ba724af0414cc525b From 47cab25bd96df806678bac3f813d43907708735f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 13 Feb 2023 22:39:43 +0100 Subject: [PATCH 020/164] Merge branch 'master' into test --- Squirrel.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 7c244d5f2..78cc8ff28 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -655,7 +655,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -700,7 +700,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", From f535f9b49f26beb5c8939e05bc888251e8cf3ded Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 13 Feb 2023 22:50:36 +0100 Subject: [PATCH 021/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index cae3f639a..a97b804cb 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit cae3f639a696eccee8017d2ba724af0414cc525b +Subproject commit a97b804cbdcd7c5342279be80e269be66a01aff6 From 51a5523fd6bc2244b7cebb5808818cba25b68111 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 15 Feb 2023 20:36:16 +0100 Subject: [PATCH 022/164] Icons --- .../RimeIcon.appiconset/Contents.json | 4 ++-- .../RimeIcon.appiconset/rime-1024.png | Bin 34319 -> 35197 bytes .../RimeIcon.appiconset/rime-128.png | Bin 3304 -> 4630 bytes .../RimeIcon.appiconset/rime-16.png | Bin 0 -> 1807 bytes .../RimeIcon.appiconset/rime-256.png | Bin 7059 -> 8102 bytes .../RimeIcon.appiconset/rime-32.png | Bin 911 -> 2287 bytes .../RimeIcon.appiconset/rime-512.png | Bin 15660 -> 16313 bytes .../RimeIcon.appiconset/rime-64.png | Bin 1723 -> 3077 bytes .../RimeIcon.appiconset/rime-small-16.png | Bin 433 -> 0 bytes .../RimeIcon.appiconset/rime-small-32.png | Bin 712 -> 0 bytes rime.pdf | Bin 660467 -> 323096 bytes 11 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-small-16.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-small-32.png diff --git a/Assets.xcassets/RimeIcon.appiconset/Contents.json b/Assets.xcassets/RimeIcon.appiconset/Contents.json index 9c932cf63..7c8a1bdd1 100644 --- a/Assets.xcassets/RimeIcon.appiconset/Contents.json +++ b/Assets.xcassets/RimeIcon.appiconset/Contents.json @@ -1,13 +1,13 @@ { "images" : [ { - "filename" : "rime-small-16.png", + "filename" : "rime-16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { - "filename" : "rime-small-32.png", + "filename" : "rime-32.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-1024.png b/Assets.xcassets/RimeIcon.appiconset/rime-1024.png index dd5c1034f82047a9a1f9e676387e22f7d6b9cfb4..422fa49033d131a9ba4585dc574d2350597a0172 100644 GIT binary patch literal 35197 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 CFbD|% literal 34319 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 LqOKnD`{lm?)w9*M delta 3293 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256.png b/Assets.xcassets/RimeIcon.appiconset/rime-256.png index a70083d6d7e5312239ab40dae132d2a99f1677d3..05298048410d74b00fab076863b9c765f82f1d9e 100644 GIT binary patch literal 8102 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 literal 7059 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 LQYln2fAN0+7;-*` diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-32.png b/Assets.xcassets/RimeIcon.appiconset/rime-32.png index f57fc500eb71fedb7192df534e765acfa3e5a152..61b91a9866b6682f1c15bd49e2b26b46f219f25f 100644 GIT binary patch literal 2287 zcmbVO3s4hh9$&;FB`1bf1Oa7PTZe~9HZLFxA%KB`5TKDlWH@`9%@S6UY;LnaM9x=L ziiN2Z1$vZ0=z%4YiuJC83c@H@TVyJw^-(R;yI!w70m+>YP!I3E@W`~CUVGP_+1+n< zf8X!_|NURHm01}tEcXiVf*@#lYKl?~?y&Rp^Z-{&S=l{sd)l0`#R5T`XPu`T^!vB` zA!zA6DqF*9ROuK&8~M17){=ay(G1WK6sxeBaiWN1VJ)erOmbxEY##zsIysUnRiP^L zMzWAfDPzdZWf|E-SrH-AA&NLS)`|fFBgx{h)o3tTFsmGyyFJm_SfkTFNgK^Jzve5XxjS0V;BG00hsn-NfQnp2@OqkwHmX2!=AV z6m5c?jJTFAX5|QQc3y?i?4mVU79s^=Ca~gWfsl_nRm}l)gbQabW(;%2b%cO4kVewP zS^!q)!kPa|}Cn%FsMA&z>kV>2-syWx&cu>BWZ^aSiz6^kTUtm0(w|88Jj_BDu%$RdCg?%5 z{)d7R0QUvXUYu7V_@T(TZ{O+LMK~A+&ZTP*fd|(pBTZm_F~D_uKBQ=PdC$lFC)&@bpf#E272%K6PoC2BJ>?J&R&(2-=COqQkGtMI z9hof=K938aum4sWSsEUdT-KiUOtIOkp z#bWXG$r5jG?{v4=wJZK?Zcuj&jEwkv&1~5vX^X15>F^_pIUG)>-7b{Njd^=*HqQ`l z;!&H8YVPO=YaIzqOH2Eew|7AB>0v0Kg$m%;bB>Kx4r+x>Jxy6J`GWQ6-<6}1(YSVK z)oX1b|C*VZDS&?0$z75pRNPJ9W&dp3r{l#?XU=$|8-`)Kca`MXbBcnGsyoa(c5rsz zoQxd*B*z-N7P@oiP8G-T7`kDoE00?d4|A>J{(*s|(4e+ve0=%I5(wo|vp#@jeq$^G*pwaueVq3;o^A|4<@4iy7Ka%iVJ&&zCJ%SEyl2et)Xu~POoF6ta|&{v2WgN7zr!(|6-eO5_P@%$(GQSDeG$`gFDlb%CEHj z@nPPRhieA0UNbY0*aLg0h<(} zA3#I|@4W##zlPN|g2=svs2{O(blv5K$90#(%hTizK5an`q*>@eczJ zJ1@`gnVDy12VSKEe*qSd2E=bJ>EhUb7Ja?bPvGDG!D+Q}R`e+&Z>ICN5IV(aZ?^MN z`2*nBHv+@}9vkT#4u?fDnH0%nQiQ|dMgbmMfHvM}U|@i`xj7uiVP|KDo0}WF)X~v_ zs;bhirq0ez7Jn8N@cDe~@9%SUb@jHot);xf?CdO&NCW`ew%Oa;t2+2+G&MEF`uaKm zj^nVgvGJ~6E$`s-`J|o8<-X(`AfL~Bj$IW%*I&w;Kl7+S-zvFdB_| zLwbE&US5_Rr>CbmJUpbgxA)VI1Aze1XjCqtqA0Aata!Hd%(MIZdzO}#*xcO2bzKGr z2eB-ROn)ZB`T02)7Z-GOb;U3j-QC47jL(H$CdYA@pP%RS^t5W5nmg>|BoyG))l<27iTN7~<%n(!7-*V?uIng@QgKthyu6Ul=gH-A zJU>74^z=j~lOdDIkWQyDO_PIzg9-r8b6|3ElJW8Jx+2juO#*u9^767OK$^1u!t3j6 zTx!>qk(DHbAeYO@Lq-UJ>$+%~1{Bh;wY62=AEj9mLdY~O(*u0-Z@CKm?Kx-7(LYdl z9-Yzr=+A&3C6b>_hIsclE$Ih^w_1z_UrQg9N8q^VLq`4|F;h5O2QAc%00000NkvXX Hu0mjfcR#B} diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-512.png b/Assets.xcassets/RimeIcon.appiconset/rime-512.png index 927c419d3f6af50a75b318e4b98ad63bf63fddcc..e509ceeedc49bdabfce67c83fa211ce8724a007c 100644 GIT binary patch literal 16313 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 aD$5CH)dsgcXV1ZxNd2tNnZnanSN;=ZJGlM; literal 15660 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 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-64.png b/Assets.xcassets/RimeIcon.appiconset/rime-64.png index ef8c0959f6121004d01806a24e4a1b3f11d1fa83..d2d584dfc7c61e24ed08c0221a89bec166d96a4b 100644 GIT binary patch literal 3077 zcmbVO3piA1AD?mS!h9AKlpZXH!3<5c7aW$EWdVcfy%Mm46hTa13KtY( zI6N={z^KGh2n~a|x~rrdt`JZlBLKcYLPfo(tVSUPJSu8EiHT!My?`hIT`dCw)qZTQ zTF52yQ0{I>R}}>!5CaMhQY98iVdT7$?SCJX5W$^ax8 zgF|z1I2_WMjNx&J9A|(8L@q}Xa0CJtkH-=SXuKnZgrne`k<&jEWKhP7qy*4bO`C&e zR8*8gA*EokF)=Zi7)J~!<74q;G8u~_XgMGRS{^G=a8ziC+~zX_4UltX0;xg(N|0Jc zP6QaOprRmWKSd#y&d^Ha)0u*DhE;K-SUd)&jcN+WKpy;z!tj{hZ2}BGYY0uoV9p9XTHwc38(&-*|3`^yASdvQd6n@8Jp+M=M) z1#(EP*y)}K1h!0%L;~bg9Z)!2ZGBTwT&=JG4>kQ*@V7JYi8dw*fJpy?+@D}_Fj5i2 zkpUijC|mzSL17{HvD#kz6bbgvK~Amxu5UlXp~FBsoH>Y~$;?p(NTBme1|3un{mP(Y z&Y+Az^I)s8MvgN@rrq-!cHBSEaA47jiDk^?yG2p?G&*&P1TD#To&beE`NO zrOaJohBPQizt;EMLucaX#pmuvH!m;yV&XtikE*eI>_jk^tTP$3J#>6|8$Cyf`mXww0_Wt@VN+{d_txtw`unuVU z1$(Vuzo4qBYOuo3>G)TcDFiB&i8)q4UtM7A+1l3TWouF^DQjzQzj6DvQOhZJ=dEYz zF63Cj{jTh(+WJ=3-#9P9n0F=E+HG6oC2j-V*2J8dXSDyc^wr=RgITjKb&qt8C@*d- zzdklr++}mN^oUK3S#9{i^mOxKS1Jl~W)9reX<&ogIt2D!AN7S36 z`LA7q!yfXPbAlem%H?wA0*@tn&CShwD9pAqJcgi3-B|f>w8HdY6Hhxm;ky)}r*b`+ zY1*r-L_}ZSYV1(>_~rMUM*Us2r+05bFG;W;|LtxR+b7)&?&#>aM-1XDjwrnd#a$xr zOLb{>{{6fr*H^u@x^-D{7pqsVUo#w#yCx?mPp7m%xq&Rq%g+BA?D>JzUU0;U-_#CDiqHS_o;t<(ro3bVy(*ptX!lT^oCN4p*yGCv1tEH%|d6% zSYO@V>sO6;2A=c7ySUgF-hK+biE9!~?ToDL4$-9_{uwRExTlwwe=Eb0Z}h0<#asF4 z1M-{Uu3c>c%lJ5amY>~-Jq)u%!$l{xl4eJ3|! z8ag{I%D|qBHxe$NyPjS$GCCTmQhhtpUA92HM^grKZb(|nf}bX7UPNlD=W=grQu=W13W!}oo~ zUJEFd9pn4MnA?6Odp_4|-w>~UeB3S@h3$Q}g2j@VY$+NUv>f zi^GbJL49^D3=4I@#D_SYcug(hPD{&`;^La|aY9m3(wP7U^92inH*U0rY}0M`)ko02 zySwvPD-pqLo0}vgUr1b-m6>^Xa_M@91mnSqJC!jn_TQQ1Q=6=j1!nVn7~h2t)CapJ z5wGT2!Le?3R}Z^f3n`wU_EtwC1_l@E#s&HfO-#6K-@ZLB--mJnv+c@n6EAHt&*nU* z7#bR`j5}GqYr;%Y3K!g#-U@v@_&ut+yDRo delta 1700 zcmV;V23z@s7`qLSD}Mv9000B90Vl){tpET38FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H121rRnK~#90?VC?bQ%M}hKkv~$(Dor1f!(SEvdx}MOu3l!q|unb z;@^1Co;;|*#ASot^rVS@4qi4f5wh;a?4i+(2X~biqX(kV_ZsE@f+I{Z!?5ukgyx z6hUYS>sXo1{(nnA1JILj7q*5tzxg1xoUggG(8%rIa6#Mq=(;F_G&Ibzg+d{zysFGYZns;orBW#=2UajKF(KGo zE|*kZDFMt1%Vx7u4y?c|mZA#aaELu6olZ+RumUpyoKB}y-bD*w*6i%UOaMhur1DA$ zpsM0y%YVHEl$6NL?iVe9tOF}Bi^VKvGo+r%=ktQCyu4h>ffe|C;#_WWa#AX`W0G*wk z!ohfI zTz?T}fr_GV;lc&!GNc9Q?(SxAa8R%}H8pYg@L_3(#p1}3BW&NkU9i7>`<7R)UP+fD zEdW4kYpc)-bY17nnKN9xc(IV8K8s72E^+$wX>?tmWmgo1W5<_TrRDkp!5crhle}mX;Q_ZQI8B_3J4uEiHIpnnG1oGMNm0eSO@& zf1k^jFH0|TWhuGN;Wbtwk>Kgmr^I40-n@Ckix)45 zMx&POKUjV|B@&6~u~=+YibNu|9F<++gzIXu^J%10-G!0GD zrWY^e<>gdXR-$Pd6%`fud_H_WA0Cefuh)y$>&5MMPk{lOz%eMn*K|{GW~aad|Y}f?6U-jMx*%s#t~b)7C>f9 zZG`H&PH%6o^zj&wJR>yyuz%$8)M{o;OJYq7bzw@#-MkHaKrrUV)5Cgb_{eNfo?Mj_0&{>FN zD~#I;upa2;%eyo10jO6oE)`z2Hx^s?72|>>U|f_O$NoNPRf+@O&Q+w5m2p#ap)?8X zm|OWNT3Ok52Pc6aKUMiDcCs?NLmX&csPa=(0Y3w|We|8VzV+WR>GNQ$2IpfP|6QN2 u|M+>iU$IGGjP8G9%pGz5V#a7cmwy4%cH-y(J{p(+00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-small-16.png b/Assets.xcassets/RimeIcon.appiconset/rime-small-16.png deleted file mode 100644 index 570edc61a5520de6895b3b954f6122630cce8f83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 433 zcmV;i0Z#sjP)VJN9QvmP4Cm>IrZ=lCLal74?R;zW-B;gEv-aAmSxul^Z6VRVKf@uG#HP^0L*4H!zq4WvR11RMG;zSvMgi2-&3tt0oZId zlu9ME*6emWn$0HL?bc4PUa#>yk0^=&D3{A9rBF&ysZ=-~kAz`Jx7#&hE5TnC$1!=H z(`Ynqj`i|4ngn4OQm@w^5~XQMr_%vIpUEHy3~AyOP66{gxA^IFYT*x_-$ZMf-u`Lw b1@wLZptr_!l9gh>00000NkvXXu0mjfV_>kn diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-small-32.png b/Assets.xcassets/RimeIcon.appiconset/rime-small-32.png deleted file mode 100644 index 1c1d3513f52c8fade6981033e7b9049e51499fe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 712 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%0D*n-zxgs{-Z0oGM(H#=t3%zDx?~~!6^op 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%0AA4}+7G);$e7@m6v7^VM?arVQ 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%0DeBOsV_%hoxBj{=mlC9YzX)>-N@yI zz6dDtagsUDwW|Qd>-t^&rq}Y~?}!>u0g%P;wL5Y8m=%0*;TUO|HD~`4IGKAP`y`5> zQlXu8AQqhzXn)iZsAJikgL&;dxX@4;zcy>}$Os zxTK!2JRp);PB#Py`~`=w-BRQXxXw}LBU9-I`UGUA#l{nI0uH>`>|M;lYMBd#p}kB= zRE5*<0!y)*Ox%sKJ-~3#rKA{>=MrZil^ro)S1nrD#eYfmsc7bW_+Vk>^a=&$ZdFJB zL?x*Mw_jIQ*vBQ!8PVB>6#z|l&i;~4w&H2DqFr{>qR;X}om9t%Toyb?RkhHb7;zB2 zk_tH}nK)PJ=0q@(0sejdAaWIwSkvisqKLCY45Lh!*>xZ`gbr*|zbAKqAP0JSZPrt+ zPJvZ)L4QxbHvpqcs#ia{iPY)GDOAmI?_Zd>5ruuXK7+FJ7DtX_;&giY+V(ca082E| zT@7nq2$%^f+;e(cp>i3d_##A|?u0xvJN_ciZ;GTE{7P>1BH>ME+z8=n&(R>o|H!okAOR_UHi*VW{EE7sMTza_-a$0wgAA7lDO;K}^}F7Go#nu%j8x41 zmVd#^GNaG3Za2TTNIbHTZcDImRJ>p>7>==o?f`#c4P0lA4m4thLMN4jlGy2826o)i zJwn@wTc!U>XOFNQ$gqH5<9h+ovMb+5WFDg;l^pNNUD4S&U0NLVX3@wby;&jf(fKogfn~hNtLc z!F)$Eqf8NH9OCsSxb%#L7@cQ05eQ1|c$o(L(G*K0wVTd2r8-qN>?)BOVzG>#{(q_X zzB@#1PTl|E$??oY&fNnaLH=1H$sBu6{s@QlxN8DSDU9eqvjN4j5jk6K7o7{~otdcV zHXQ=x;P$|pLwdpQZg6mEyUA|vaNLX6PZlzrD zAV;*LB%v;sJ!c@IB&se;IcngBC4U?++qD~?pUKmFZPGafLLamj+lnGa5%kY8xf8vq zvc*b#EP0D%dEkY|sFhB%I>gxm=m&vWz7F7tw*xqZFWW&qW%|1!J+|Dbf@WwBi)Llm z4SralogwLO8IV*<9Ov{@kle=E7v*n>K(r8n3ZTI1V%8E6enKr%p+!nevwzWf7K*m# zuVa$i_({rhlCfn_=_eEA_&_w2eL;&3c4`py(HZmUdZrjKd91|-FV87df3aQHIJ4w| za*V*+4ztGuB!y`Kk`sl{D+UsRVjlpJ5jKe31546BMf0WR?{!r&i3N|I21Y&hnEttm z9>d9*$7VK3epBZR?vL`F?SE?JA;!=0YG4a0f8yU+M59VTB`M{UR$taAlFhAl9deNW z#?aARoGNC{us6$Ti5=BSo)Adgs3LguAURi<7eAt9KmPyLf=D+fk*Fp(C+x!ljZ@qt z35gmfFqzAbQ%8mGgHA7JqeObF5IWVL%g8bnQ2SW;CdNv7+AUzz0DlKqW!`E-e+?uj z@@tiz)k2ghmp!p_$?pn~uO*=vgk6QOjdw)WlbrtKMDiJ7^@EF?Xh;k~GkAc) zg6&>LYVEHi*BO3c++^9l z73yRFZt!i`ua29>5Pz^Nq=Y458d&MXC>0bxhix!IaR2L26)<#4{sn$YkeMSNklwh2 zbDOI&-+%xovk*|t&gn3c;U47-0EzQC3a5xmc5C<}9kL-kS5-V7NBto>GnmZ+_BZfC zDyfdb6`-EO%UGRijBaki%PHwOuci9Mb*M~)Y$bB>7ab3GM1Lkh*wnwD3#u8Q!q#Q~ z@XF1XdjWau#AC1z6zI${BVhZXd^`8+kwr%1p7@4kIlg^0lYtS&;pPN=zxD+J16g3s zvyHTCus@#UrFGBJ_4s;nC^bH8U?v9SIOvPL{0%D@?2G6GnLs|2F2ZhiKW; zn0ylef*#II=6{;4cxWIF3Ax?}`VT>is&$6E=s0ZgYH)Bm(Grz8agxvWWpGhP2>W*s z^u~ZDk_4@TG7PFsfS=~F0pxgmf}w@rgqO~R$vJ_D+O;5fPJ0!f(9-h^~2UiiJb zn#Wl7{a=OQq4$Bh&LK26FfI^F_wu6KTYJ1LbnrseEM-~`YO|GjbyAB<+NkQ@Lc6pr zB!fC;PVKDQeR!&P+>YqgL$R9>lWMne9LBfe$`2SQU8YL_)C(bY!w*KGlt}}i@f;R? z{p(Ej8Gn|v2F1vW2kS{jc6uOww#>Dey?K78L7u<(nQe=IT~5-neLUFiS=zgfD`t_) zw*5)(IiSzJHyw#C>bd!b!t^xPjqr>_J7Ru9OLzthpQH8)i$@;K2(=rv9YKvQJ1Gzb z`6AGh7n&LiUT$#aS`l)xbOF#M z%KDdCih0%=Z|p#wM3}RW#Zwyi(g~?K$F+H*Aw`NZ{mBR&bA!(YyPF{)LLoAE5Ryxp z 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%0D>-cnOf9l&GFZX=bQVh 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�%_B>`q)K(@7?WWM&HFP@rf#Jq_AF&>oAP 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�>L=z`fuy@E-m-I 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 |Vz{?y1J|(5i8SNaKRXWqBoVA`D0(L6MUW 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%0Dl6ZiUA zif%gYtJ(UyOyM-n*krQw@%w*N&4VB!K`KrU-hBMs-#`B0pNor+X>|nCf&kiI{_-D5 z?QL$;;fTS@bHjY@f_5xkftl^_$R)3bGV+#8}w!4CfOOR|OJ2KmA+kM;`KFyhX zOsY~f7j@@6nkZ0wod%6)vJQ=v793HZl$$4Ux&+f2oo*G)CszNiJ-@5CmlSVminBWV zpqEEb2BJ1H>g)OP{`$pluJ6CNvs+c~_uLbwe zjkU^he)MMl>38MsGk7QYQ6+?C1i4jdul#5-Et_%HqSD<$eVwZ=Q<*NTV$(y!;`tP4 zv7knYZCacP`mSQ^>4iBKl`vc=*LL_!gW?Ru7&IqWd2`RG?Ul@RvAT~%6^KVAzoX>g zlwG+ngF%|t7C@O)nx~RJ@P|07z3z-}1a%~7BSvWcpX2h{Z5+Si@H1rAW3P*BHA*7I zB2pA3ibW*Frbb!4ce%=PANQH{w2XIWCJXFf2b)bW$Ra_oNRX>sXS;P=-!`ew$3U5lkvnPmJt#vv+oW{eHOrO4Qmcn;Z1bTm3VNu?>58arf!; z=JWLM{b2TxtDSHO{pRZ3v#XE3AcQc)S3b&Dp$D|=oRdcwnb-C1{da$ojg~(or|s>8 zGD_+@`R3Khi=T)4@8nef=I9lC}QT!QD^Y!w>1wv9UQ% z7Y<0awY1Cv$2QyNB$e@c=|GU*a3@2+Jh3`KIUbL=un=&2;t{$Cp|r4wyVjFrq(ahF zUYd&96ufN36@h{B2NbY|Fm8Z4t!*$+fgo^4vfr%VzVJs5XE9jb?kgsv3iMAivSE`b=~anQW=&A zL1o|(ADZ-{JS)}&Z7e^=$UxHTzCb+2Wp3{OIJ^2J7!%nT8@VxT^Jp|P9$yZ(?jUJ_ z^E2feB{MfGH;^GHu;@CYrD|uF-`dXj$y0v-Gaz2{;*qG2VYC&sT`F$<^5vgDeEz%3 zE4q;sMizNx+TMPcKmSW@`am(x$;r#V{o_AbXA2w+XItSYAauLfdOkV-45#Mx^8#p5 zs)-WNZ(1O%2Qi(Z9rd?#+CP4&8l7JMSyF6}(E_-J2T85ExpnYO#>o|rV~J9XFLP2O zOlNA%^X~XXN}q(nR+uoY%s8bsF*r4Or~$h=bJ^OVxe6RHXo6~vf@u0T>7yYGnPgwd zo}}!3jI@Jjs#x0{&h8@d{8PALgT<(vQ8R}@fxX@!AKquHvYZ6Sd)lPE{H0?ZwZ07jaC=mb-w z6Lmjkx)CXW$uyf;_pnqnqh+VEJ`z%ceEBL@zX`{jbze*}dU2)Y@^Znj0h&gD&E@yG zL>+D;=&T<}GDuCXQb?bQ$OxZU)KgN*4(t)O>vxE z?VN-t6V%D$mk6qmuN)K`Gp9BuNE64+CoS_vnF zI*KdHt0?3~LaJDHr&t`#55GwpO}9_vlD&jD;*zZhR{~{#d`n90R_|i8{oEInJcuHs z_pIujY#(r9GcI>SB-BBg?%Zb!;|*`z?UzHiZ5Iy5``^{Om-$Uto&AC{{_^Etjn=s* zXh_zMzImK>4(!S?6dVbry3P5&|NFnU-~R%z3hqhAo3g%ZZ=PzI?Sxq8;>Cm6tF!A* z7-d6{BDh5`n{lB{vDIKW=MQM9RIS-RMM=XKGQB}#ZG&H3W66kR7pC3m4H6Ov!eF8W zL2kDOx1Ih4id!pd(WS5P80+*0w_)7;>Z{Pw5=lmioAoQ*-lw7!A7Y3??dL!LdUpQy z$rBHFfy=7}vAm*AG;JJ@SBbC!zmHLdFCh5*i5S0$5jMeA5IhT*t(yC?wk@THP;(&9 zq|yXEB$z@VY#^8srVFY$ZB1TsQhjL!gYO}u4i>T1%ozRxwk;BpA56!z)omUUx*jP0(jP%I|dXA1#x-e z@QS$9qZ3V(vT-WCd+-gz|EIXTdfQ0P zDEt$4SA}gjoZg4idm(4odmmCFDamw^ZP}JAt=6%;3&)EEHV78TRf1e3KOjFd@7@vM zH^5ikbIx-nPn~vHvpNw*&!{=PWBB4aB1s*I z%8(J-V9`-}X1UAO;61Q}9jqtoC#Yj~c!uH@j$3lI2`}P=nPSaN?|cZ%u={y-{z-MF z`Jz}=dyJ4!LZWyxMrvS?v(--0x@?c%ib~V42czX(*u3m$!6?6~w5F@u z&gdOrhV5nb##MXt8Y6vG?;7S1jGdxm&7Ke24$7!zn4Nm_%nfHzeP#Mff^C<| z0ftAt!5go(E}=3-+YD1HqmC%`;kpvAy@uD++?8a`i>OP{iKb0q8ASzZwLM+mj@I`K-vSn5`6kpl3^iUndt5DEROLuF zTeURnj-RhC9)ZwGW$WZrD3)}IZ&$_k(GP5)pi1fhymYku==dufck+3eW@`23Gsn+< zI3i9@XdE+P!?rzjf^BE~=%+Vc@?27z6l2S3B2^n8$*MIG6mXADg-+qTaHSZg)teVd z`?{9C0=$6)B)B?ChR|3!J|bxs(rvJK7_UD$;Z8H>UVK(H`ikBfj$chycN8CzTtM<6 zw8gg%e>{Hu_xkx?t$2qKHk45CG?J^A$_7T(!Mprudvp8y>imOW%d-6=FLhAdg@FMM zk+rcC!nW^b`){n!s>=1j@G^+zyaezDQ|(LY&~i48mkCniG|vkV*7{h7mgg-Y3Ku~|Faw36QdPQPlod-tE=MtZ z3|iyh$U=fqVWI58pw^OYE!knXv)X-n_w>(n_!=%skh(CX`8?0FuJ4av<|`G86WWSC zkCH1z>(P``sVK5s+g{u?8VgDE6r+t%v80ZpVD@zN)n(sSTJ14KYYAp$tyXK1=!p zspwU;1yx*~a>fI|r>kXIhvcr?txX2;0Aa-i$wPwz2f1^1Q-5!L|YH{{R zP(cY%+~}+^yzfpw>P{xAV~T4MbRdXzMQKu$LDRlwE~4a+?Y>Nw4^Rz2+WDd^%6%wb zqS76$?xyFzKs*`F63mUH>qyxkC=VlTv|{-2ewh6<$nM9R&-MNXlJ7vZfvlWm4_&xt!6C&S7;`&>`Ye1vO2c? zEiA|trK2NS;2ZtXwWPKP5DVc`RHSMHPHa|SNDz%6%=7(iv%9Sjrs>X`gPSn9H0`w@ zjq*kH=!iVcv8vKOIiYx=R%>2NH=jQL?SK0>zq`>Ie1pQ2@$}k{0OldTKnOaV4#RC3 zNQQ~?1wpQ+qSQvpPQIWX9W#ZJ0{`-Stf_T@(={ztv?c@}QZ@?(;rs6^-+hM^3o_5b zJ-5(`Ttax3kCC!nK?5iwngPu*&YcPZ7YMvxEYP`QI(I4x5@e`csi-s^X~xnEuOw}T zQBei=BxS&gO`MLvvQ%SUOJ5}2w`Q0jn00)@;kZY$H9<}}xoLS>nC?|;%!yG2WwA2D zFs5d7p;z`758LbC=KC)M*M!_;Iny>%+Xg2GHtT8jc$z)FfBM&G_bA#^fCs}Gsd|f~ zY*`x(=l4H8{paIf{yRPU6_|m*49}nI&Vm&hFnfxzko8r2a2Lc^6cb~Ws%3#$&Hhhu znf0iRrcwA0Oggmz28{Q8->{7_X0ZWd%u-MV#Zn=aN~$UoC7r6MyDwTwNv%X0Y38DD zMw*{HZ%K$NWc`N>80E4paDnVbPz!kZkAMPYMv+RYc=hd1U zMmxjID;1rjn!KDk&3!buPqI&f+ygI#7n@!FssVX&5htZ()Q1ycxWlrcEJeivZRklW zS~zYNwzs^TK$4crf~Ci;852n!P&8D42IPLR$lK0DRz}650?P&XA;=jt z3!0586%i>BzMn;%O||Cy<5jg#5K$ByPr)&-21+Zdg(8y-wyW(E#Qo7R13v)P;<=b( zqFPmlhaVqfXw|@KE-SRp&L{-o2*QH!<)lndb3k-n%?)=Ay{}x;6+H{W&2;`WzxrP9 z?5m`QpbjStYPem)R9+0z@xzC&e^0NzYtF1(VR>2;__`>8COeil^V&NzfMi_A+EA2y zfj2qU5arNt#)gx(<2O(ko%U95u1){a@#nk`Z)QIza%K0Lp@|LL}GF}xx0Em`T2 zv6OCY{R>pZT`5v{|WXrUOuVD2s+Ma2p#r-;+~?P z5Av@!_kaEK@!y-<=T`ETB%D@rcJuzv{_ubi78nI!70tHo`dpFv0^6*Vxkh6;U3^iD zF)x8#z}!4Z8MFKVP62lc(2}Ni+3d4XUqZiQg}7YR;T~l<$tHIU50e~#-gI}xh%ZJB zkY6nEr6MPbO;v76QqW7TZGXw|ZSX)@fdsTq&)6E~5~PoSc`~7{=Yo_17m=iE+c`x? zNE!4Dk8(jpHKGB3%1d2IX@}8Ov$cbfzIt6NS1g!(p{O8bt40PG1+8HKMalCQkg6E! z&~$`jK71o335@F{1*pKTpA$p_!RoJ1$Wn<1kw{7mHs0;u-GBM}{_Fo%Z~x_n`wD6Z za@Pc8GE&uEDD{=$ZM*5?^zyk{vq;i(+@bA_gU+5e&Wm_hKrG)`=Gn)nyCZ3v7aM|- znDylXQYLwe;tEn_AU91HfPBvKO^~#%C+YNVviVK(FBmDosU{o~c!^?N-yi$oh2MD# zGlxv9qHt@fD7_dCFAXr<7Nj_6-!{7ss=4CDP6=U7PEmq#RDD{m*eL3VQUX`3>O;?; zxo*z#ErN7G8#cFCQxc$xTGssLf5?bigs0gPcLGHI>!234C zNAd7BhtOQyrTKf!nbhb2Si^h z6|f!E$d^}tTyDPu@RSf$5Zdn_p1}aYPC2nJ%9+>P`mHS_FF^$eBGvtQ6z@nbI4KHM z%%k}}5TL3}IL4Pa-!YTT^^?$QqaVUMH10)8)f#P)tW$=7i?RNWz@%&-D_&B-v9CSC3 z{v6-ctz1@PMeX&okL$ya+4|4^^bv>!IDzGTQHv zsLx4#=up59g49>k6c7ce2(VmGHzteEhmSvbWqekWk%}TnEzds(4ZwUfYX+h{Jp5;O z`vcy#Tor&M`{|*dz5(`xH5uwQ4!SGrTg0lh#8COO<(Y$C5zYQ0UjytVY#VDAJXZO$+U;Tog z73EUZp@orF-}}J*v*etC@+3Nk0AZnz~#54M6kF{zcS#W7a3U*fO1QBU*}T7tRioq?wm>R;@8BMVgt{8&|rs((40pFs*_Z17IWCjl#<4qye}lFy zQQUVv?z^~)6t!BG_uZD=?({aD?hKIh1W14YgUKa_AcvfCN=~`td!{@954LQPkMGsL z>R0h~lwK0&kQXxBS*S+G2xH7D4pOD^N$zFE9y z#dts&XbmVe8tITSVR`|nhw*lM_N`xjh_f@3)TmmY%I zdQB}Q(G>){s4l{A&GQjf%d!Bo7!c(+3zIZF=b++Q_MsP@d-0`iZfM$J8C&3-DCj+Z zv3H;`Y*aK)X|wB=ACuy7#H4TbG%%a*t&9AM=fh@lo0X4nT~T_iU6>}v?ZW7=Y42TB zS_{#cOHz!eB1V;@Cq;ETnLfg8qGS_|mm0}|i!A3m&MJ&g!({9DQxI1Wt$-zQajP3M zLS{jHR<=(!FF!wg_**@DkE8da6%J8_|9t! z^e;Hg5oKCt4`)Duwd}bpSA&6eurJtV?fF|#E;%j{fD=M0pgdwCv=UOJDA#jW+mjz) zM1mOO*C7?C+SGP7I17sMX8q0I|M}mpyW8K_4-NuiFI24%r9e?LolG>XGRXw0<^^=d z+Ok^Hn)u;7&9-5@v7H{WZ@On788TE+#;?VP)UujLa%>o-X?LnIk(JW4+Hro_uRpGi zKlg|4!tt@`VE?CrlyH0~N~vuxiFN1%!0AiKgV<9Xr6^}Gfz|Q%diJfFe$ASXPJl1= zhS69~j}XTXlkQVqzmJj|TyA(G$SE~6R4Gj&;BlIyH+k{mc<1o9yxwKyv!qS}e}7wih((j%btP0(b~TDKtgkDw>Jqt>7(Quoc8&Fd(O=KVVJ~ zm#BEu$QSvIh8p_8d$_1F!~80$uXskHr#>7d@O-C)ft8E%f^ zW9SoA#ZY?&sa$X62it0LACJ#5q*!XzJ&&t90Fq8x*kE4YOr|fUx1t$W6eE&v>GJ;1$@pYAkWH=B)e_^3Z3lY;t`3H-tj@;OGi;Ee97S&McGy*Hg=HcU z#*$i>lih6ng<%4oOL1um_W258mZbu?#c&}y9XM)lPiB}<)h1zhjC6C|mGr7xaAxFs zTUkScJ15Q-m*H$Ux!?p?PzKq6i!?HIz2kAdGt4FWg+Uth(>S?UtiM+6J4K(1a>t9U zqRwbG;e-r0p#mrDxgQ?E@KMKbf6z6!OEAE#!3@8N)nO3K& z8Oyjx_9DLsgSBOKUT~;e3&ofrg$;X#MT&CjdULFETwVhgUH=FIaQ#)9UnmADLkR|M z7oUsn8EUaykVA#CNvtUd6B7hW^rLIsO}C~|cFv0Fn>RcRu&f?j{}2U`X5j`lZ0F?i z52sf@LA0{^e~!y!xozx>!jE8495Bx_E(QW1K#&9plHedx6g5khEqRD+wYxo}yHbif zo3xW&q_apC*(6VpWu7sIszR|;C6K_q_dn-+C+QbActbd$VGk+B161^itE2NT*T4G~ zcTcdo>HGsIQINZ=kYG+=ZjGjES)({zGf%zpNqr00ucUzPyk^ssG5N-@+0a0DoYYke zAjdk%uMB4dT0^aH-I*k&a2Vo&mbbb-2QXm59j9zG)K=3HxK3?f<{(LL*&n~j%eQYo z{^O5-`R{1*Zf{SAo8fBIEr1#Pjiv!()ug(w4jx=@fq~y1e{M%xLFl%asKs|Beb~uQ zU%dMH@BjKAv?(ctC<9g(S?`tWZ7_ro3kFHm3#`I*fv;!K0xx3o=dM4858!7}_h>HF ztpmS(9kj1#w$r2nmQO^v11<{M$M76@h3(BnB^Tt(u*+g{N80#(91y}ZGYseje?+R9 zxWP2)9&axnlN<)MaQua?_iQ8c+`i)?$a*LO#rUqT4+=x&05pmF@*a5#Ce$XvHew|&@Tqh~cqT;eUu>OSjTA?Q(yM$>CA0I8-nd`X`o>j4axul{uV@YjBcsau>~`~>Y_$ivR5 zMGhcmU7wg{$qNBZJH(p>;TjZ&IRV8qnTVGMUiWr${q+yO{QH-`{pZu~e`Uo) zR(ilW^cvS&!DKL0?dZr4H*T=fZRB0lq}vpW{#6)u1##h>QQs7!fODdMq1te}C@e6O zrZoW?335kK`^YT##$x%-bShTp7*@&f0V^OVCx&@o+AC;ZHKvk`0V`}Wm4IzVp;`0E z^222H9hCmGt^^b(ND+KSlv7@WK~-LG;`tl62YOtqrI|)w)w&Xjl2zI01^v5G^~o?w z8sT17wOwxZWVNH)LxWUyaOky99B;)-5zSa6ScK{6eDiU<{@x@9vOa>rqM+$kUo(5G z&_svX$kH%z%1NyzQZ0k$JchPpCC858 z!WSLC3ge}$rLI3I#_v1%U6Nn=(E<%fF$b{9AYAr`SDX-vQW|Ghe!PL{i9(lZd5xyd zvcxn7Xoa-@nx#0l`Hc5H`|ABph<&x|sNl$?&ww^Pz%gcpk%nWlz?MBGM#qzNqtk8Ghjed5i zm~)y1Bauc6xYe=TLlRv$!PX*c90U*8lY1CQLCR>hjSG2a+Di?auXK39my}R?5)mj} zH%AynLF%H)fox}sA6`EEb9wnQ@D%`z$x$_Ycc|L~H(ZtnAJfrW5^q&&44Y+u8BBl; zLGS^FRdq`I%OJW&ntr8WssOAnDvSeG>fa`{rRW`aE6#Z(aU zC^FA@|`A43VSjr%h#LJ$CI0H52H@PBss=PZ zjG|56zb5TNL~T319E{$C?IRRYQ4Sq4?)1-fZ((@Lba;1q`)gj_BW+QL(9T4$?URE} z`W#il4YwpbcD;2F9x8GyAUhb3qJ21Uckn!jkFbrZF;TR!A8b$)I4KvDp+gR<)tAZg z2Mi$$N0JgWH9wqX`O$dshQvps#rxISPph*>$KOKKIGSGnrkp+K_F7aXmNT~;tYRQ4 z39L_&ar#Hq`t$Sq--7Nn%kOYPQNaUqo0PU!q=OSLzR-*nI0v$VxSB2WIE&*#!^nep z87A8xIt2pR&eFE#6cyAP2Fr$$nD0L26vBl0lVEMxOO{LEPJ7Q-*DCd0AZ8FaNTZm% zsuu5IXbfu#qSwujy7|SkJqhM7t36uiNGdAo7`O<^!qZVt{P46LpX0ffgA^FhbmDo( z?dXg_HQQHkC-zEEO2i+-rp+eV-&YkOaqI#VE$c%&JeeOqp1=I_049J0*ZRQwgX52# z{2Za*`ak<+w%bN}MBxWW#x~d9H}<~oO|qLiC6Sc4XqPByEY8@m6+1C7Fk*XVkhw~L z1PG7^2#{Az4*dv0fDi%eju-6i`v0nPz5^)`3e!3D;!B7R$@rLrMkCMji+=yMKic4I zYeYoSDYOgPQmJbc7l>*fEuiwDy_l9d9Sz>C{kuv~!g0&)Zqw(OB+bQmFkufm`c}KrFgi(rYT3DD916-nGbd4kmo5YuDQR%DcxAfBG?P$tLNbYJOc74E zLd_?&T0@kgR(_T?FZ~$i(x({&xQt&G&&9Sr zVwnI!2PVmi+x~bHXP1Cyigm+iJ{oTVfA-BcBtSGsmRa|u7vJgD5yM2KQiG(tB)zii z3CBe&n}Q^5NG|-*J9FFxA7)wPTwmNIWK4Q28+kH6u&1m-EL_^u=LA2xMw=koIoauT& zF%C&vjx)r6Ve|y)Q54w8lf+iLeI*;Y?RFPezYm%STj;oX}-aw!@+6BwY;gUcUdvvq%6)at{y(G zu0JN(!ZZrdi0d5>r*CCryHGUSLd>ze#d!9V<CayIB<}XuEd=~ zbSA*Ig@Zq~-LY-kb~?6g+qP}n?AYqqHaa$6?pkkp$9GgUs2Wra3g_(oowF^D4C1fT zWk!{`r)DYq(+| z)8#Lp9c+G=(Q9WXO)Bb=#ONgP7o`{^;$wG^WvcKx9n4@Z0Sb!WXbp2ARh8@E2Fm2! z%|hWa7;Fx;@)hkj1fxdwW}|ksB`;`FJ^~aK-EW=5;1YB}?_VeEa6O}wveSHj|E=BJ zShRdmUhoI9pBK+Uu01UmmjbGhyF#5T-mMmND!_q==UB33_{W#c+WrQM3$QX&!}lHZ z?dcTb{Kb~|3E}<1^@eOz)|E7$ISwf=ctUj=inPdC#NxXxbXv$6x4bQ+Nuj3FLi&3>){@?1r!d+-^RO z2BLb*Fifh2`j~evaN(s`$>l&3ux4@=deq5nI5@SmySsJ0Cc4x?tydrc&HZ&U^Dv{8 zpe4zq_%o*IX7N4~CCA7=o0r7$&k`KOdLW#Zy4}*%k2~A9xx1sXzj(m0q`#i8R%T5} zK>=9$H|kjR!L*Irggp}KpVMu^ofImum}ql8`Xy^^xG^?Jd7p2N>&^4!bTnqoQ?-g_ zp&>Yvb(~%sx7q}HcfLURoGoup@As9$Z)A;XZPp3ZD2#Q56&rP8o~_L8&zHEhCHYF; zs>N)}VV0>PIXBRW&&Rs+*<2W?(^dzUgIr}&jX<>$M`M&O6ltKa%f&bJJ>tGU*gtJUngT9L*6z-~ z#C5OOngfgiuw&-$i~?Vm+P8%QBmNO1j-=D8rd1PjJIWLJr)KsNJ&lfIr+HB{=F~mnDa`Brye4n58U!XC`#xd(X zVWtfZA87jqF^-nWWjGK9?I4cCd^bvjRfDF!J2dt`PIaq>eKiQw9&#!hxq(^jWQfE~ z<;5#DXe72tVtG8^J1n{p`M_o{Le}Vpf4EXoS}h~G-YEz>#>1f6je&|40oy+D{F7X` z2Ouj|XsU=>xo!5Jr1N?)Kzt#Rogmr2sX7g2PT2XwYFaF4RUZ+}kYB|I`#T6Y=%uoY ze`Q>`Xm)d^fA!y`x=hfe6tiel}FaAo28e3U84V|MC{)7!tn z=bhhjb>BNsA9X50DzdB+OaG(h;SIkR32%NU+}93s!_Cg@H+~&9Mi-n>cUn*2ds3Ah zzwQn@JkCg!zW29->;3V;blz3%Rd10GCiEvqN&ooc5^W1U5hY-*=sk;nd$WJ{EURk6 zD$Em+%U_F*ruM(=mFLA}x)Mg6Liaat6HB?FZ8L78DesGf#{IH(F zvB^RiN6#);sozFD3^YwV(S+sN(~*fpR0|ZJ^n$(aTmS0D&suN)IBd6DeH6?UwF=YE z;LQ3ug1Jzqxnynny*+*2E{Ri&Zw>AqVq4xd8N9D;Z7qdev^ld%8KjRmTnVI%+;qG& zcDLJlzj-VPb^1ry>7T;H2)zaWX@@=AF!>l~dS}uy&E{318~-HoYv+GC^XcTm|G@m7 zd{_P0>eWLi>-|U1R3;B=27aLxwM2wMUvuZ;N@HfKUOfGnC{`cc zpri09eSMUKY5LEB+<@_3C`=Laia{-B%O+^BQS1t*T#ADDlJsOw=N<|<2g_E-xlFw# zWFo!@cg|J%-+2cNUwd4$lgu933qT=pThwhk&x_WhX&rHr;0M2&F-vEN)!fb*>MIT{iav6N= zO51z9 z2xF$11XN;Eloaou$f;%u1f{KyyfMiuoL?MD?t7a;VnG7DKv-Cn~X^#>2!rJ9U^)o;cUQKE; z<9)o~pAAeo^-U~I$&J9$&#HpN8R=g-|6lv=^j$ zxsSnc;2FwJ@UwD2P5rx$_2sr!jV&o7YX=xoXx{I<9|lUfqReMkA%Gq=!i zej*4n)p#0|qKCp*jN`j|*}JuEu)raSxcx#bTJ>C|!OGB~>}-^%*-yqL^a*^XIFaxp zK|S?gtN;ihOc~S?#OU>78IsrNqk1gS#Ul?4sDNN0229L|-NL-QleGm?WxHA{+!Ge9 zk#(*hQGohDD~plbsXAF!YKPqhOzblW=Lm_}IXDd#3m`+lZ}kdX{0Bsr^)QDG?j$6C z=KO@~g~2v?XOT@EIp?YD(;ccMD|V=^DxS$#D%G&PNB|eTz-D$E2&#*$xNiL(h)GPD zcqZWVlo}&y?_UTeYL10@V&IHUK3jCU$xtTx^>Cloo5%BgvqZdoi=MUcd~}*}HRV31 zXG_EhI_Olmajb)xV1EqWg=d!)FsO0;@URWz<}q$}hi(+q{Y*ut%hV#SF?C;$gk0AJ zgC|z%Q~Z#@c>ht<%+bcZp$^Yurv^*kZCtP}yo4=$G(Ge4M}N-S1iu9?z*?P;lj|C1 zURqE6kvXy(o1C%R|8A;r5+H%72Sx+;GV5D}a%LM53qA&MM9Z`=#pj7z(naLYq2;^& z<%dmQ+mKsnx&!CNJvR{hl+lA{IxF5}H_;!J^V((T4vv=|KLrC{xSz4LMZTVn?ax3Y zg|H5zUNyY-7kWKg{1nj^tUn0M(g|KsN`sQ)u&o5Lzmdu)WV-uQn=#a;Kah)iOIR0H zOZ()u6Us+ygfcNdDCBP`iN|ZqkiXSNP3fF%zmjVLW@nDg0&Y-Irfks?%gXX%$+Tvs zKgd9+>q&&|)x~k{NscR57!k;CYXPmGF9IX=Kqv#FQC^w*G!=Y4TdJDYSV`!t*a~4G zXmhamR*F#MNPK=WecIf%dR#@W0qEwxumvirWv#4B&ABG<`oz_3Tj&^qX=Kg*F~m(3 zu(Dc`dkq(|t(Pvv4AG1L1@P}XF<<*;R-YBw=Uz_|^=*LP80mK?q7tg-d%kep%k9&8|>|BzJPV^aDqlRs+^o&b@y zS)FFv>xxq^0c*3b9{ZLQl0mH;$f15$!Agrqrtsg{#~PezWUkf|r1a}tf4$;3Cb-ZT zMXZ~JZ&6kY25kg;Q%^}mhilquTp@|7OV{dGy6R{l-^fu53Qdsl>>WyND|g%FxcLL> ziI*-sAOmZJ0mT7K;gy?yhXXMUd zq-6w~h+eQ80WAY-yE@&Nf}ovz0zeu7HdW$B73WkMnZ|-`#%sU6F>}*in(M&_nlVap z2D$JAHFZE~Sr(~MGzark7m=AgxrWq@1?z|xmt#ycDdoXSJcgm9R6(#EcPc?><+j^r zO!3dtG3S8<3!TgUleIENAlO;~Uvd(T$)TI~4N|_yxe^EJ17bjpWmOR@tQoOLZ0k%x zF&vE0{V}rq{O`RR`bX*%T8Vs)hX$_KTJ5B=wQl_SWfGdX*Oh3R?qj;Kc)C{E1!N$? z^||x3?MmyoIhQZ|fz@C}lvHTM-{|;N&~w_kB8Cpu%MM$?LOA`t=dw)n3{%0=DL4el zr_SH`DNYNOh_RFZ@Lin3b%|LKp=)~|KWLZx?T5!3c6j_~4jZz3zxU3jchoiz8gtj2Q&XO&Kgw?Jl$$=%QOg?JKi~Df zlH}#Ok*${h`eR;l-Vj7S^<`m5Ng~q0=NN|9Gx15`0Cw{l1T*2y!jW~$=GPl??hL7@ zTT0oZ@q}8>Z*I`Zs$wv7)jenDqi`nrxNuYNMK*0GBOm@%^ zYx5|=A_~y>U*fipY1WAw*VMs|UBqdEuY4~DsSO`ksp9tKMH!``^& zM8h({tlIE;>LKiuoblb9o4Rk{^KyJ4gMhMaY;#DuPM_K^`_Wg4qSAia18ZAX3~nj; z;SZ;oJ~dzm7=`sEvhc58OTVg=$hx&$TzcDZ;zh zTHnSd3Y#;a>()mxGfUm>{Gu~vADiId#joqVob^j=)?BvSrwO#jY2dwrTP>)(!nnuZ zzsyfsx4`cNfKN3jd(% ztfBraia}){nNf~MEVgcpilgqG+(fckJ8`_(^s!~{3jfFllw!nKk&;oeYAd>0t1~L$ zZ+76t0drtcm6qUo&_OjPbUK}oAJnQTtya>4IU?95K5=AEk zhy`R$C!e!AR3XHy-7I#og%{E<@g(`C=~+qFi_WAm{2g51w$3+wPU3U=Eq$z#-=Z$L zQ-6$lK5 z(F-=*7cO}|hk4&n;3zz%{}tW#yJWvUzkT=L(w8ROIA0c+qgwLMn7TXv!~fyi^sU4; z+sMb*ulclUs|QH!ifuxXwp~XlS<0{XZ3lnKa#eo7ue1I0bRJgRZG}D?%8R;=g&VYQ zk1TjcB<}xE+tnY0h>MHgK2uzMMN<|)@{`cUlcuX}Qzeyoj=GniWP8bW^xt&0R?&q@f(+tes?dY0EuA6%AhDA@VgH|*Nl19pJ*WUx~1lc+iWE*Gk+UHpp^di$k>stpTR}Gw;k8RRd zoAfarN1xh8%t1YeCIN1G5fFZ=zC;aMR}5rICg~}k4Q`FVFQ|UtBdARVKAo|4MKo=D zvDajn9Jvkp6cptQ;~ygv;g5cre_a!~Uu^)}#kPubVT3eILE0DRUy8x8)-PaB`b1>h zdmF@Df@3Hb;u>jDW|OgSw(oB$e+M>O{@SVX$c1QBM-`VuB~`5XwRkQWI!8<1uy8YAK8OP` zJ|K{->beF#rZ?>YD*!3mqF(L!yy^6(iKzJ49mO)Z77|Mqg_D&iKY~-X&`Ee!o5 zfn?7;W*Qw@8#?32!ZXPfLIwg0+SD}LZh9>=vxefW-NqZt3y^BUrpq88oR@wK)U!7N zv>NV-;L#7~CKS$ZT_>dG<=@qfv@0p!y#F!w} z?$JG5Twenz-aJXM=5k=P{lhxRh%t7PpSzs*&Qoclvhd&>vJ142`ZuTn+_t-im}M06 z0-YPv*M}%5QmV+kjQSoPVbroWm8rNZQGh;`qf~teG<_2|+lNzYvMiw<;2<67ZLmmE zX3thkh5Xl5EXQRem$a^TK(Deo5~P{vREphE~i)=2zfedt)dn?4p;=53%XNuw}zNY*NamW z*V7-fZL6oYNvH=CZy?3?_21C6atkXCtPy<;*w#O=Q7^;uCxpIh+R#6gh%hC6^7vX5A<#P9j@8oOspP&V4jM~2? zqe3VMa(6FUM1YQtq7A{7FX63^S5^8XpmmM!_e;0R)x#%BJ1fCS*^fBfdi;5f?UXoN zYWKA?#UK0tZ;>YERcg%gJ;lH`@Z4=~|Dz~LTvZZId8+?tvavO2aSLhT3ykHjiuq~T zV(}#V>B+|Tc00l{v9R>syfN-cVUZCnPE12R>&Bmr|HpGJcYVS2ZvHNwJ3j$(C`ys2 zwRB53@ls@Mp?T~_MBLnPhRF^9+S^kG7cKT2vYaDwDFW3bt|6hsAf2*`2rNCzn9%JY zAQTg%mkH}e+>INwN01OWV5m`JhJj5uz`QA^+NX5Rj;J-sWzLYZg@rB>wla@vZDo9r zQ{KzYpy0~oR%!}$AZ&sy8Z~7E%u_WZ=eT45@6b^eRFBjXXpF=ROHk}4ksMG8C8>xH zIdaAhgzYJg#wsKaKg#oeXK^XVk~dz0-rq={qAOAy?2C12_UPGN5mL!E_>|q{{Cd1E zPsNryS{?L!hwH>#r*QUi?gl>vTS9vUGvZ`aI-FdFzS?l+sjTj2kH_n%}UeB%53k;TJ!;{JD3O<4BYj;|l>hN{^g53;= z2}Gri*B#D)P85=_pERIj1T}xbYNP_`Tn2@^uuQDj^-_ruMb!#xq9VFu%r<5HRtcWu z$myxbq?Rm(pY~}Z1J4BWlsfh-vv7s#g4u`BPmE?zRZ~0 zhhdld=cDM-4lT4u`BDz~dh%`9`cXDsw|tZZ;f{HHtoz0pb4PtUkb~f(13ypi-w&4j zvo|el_C4{8)P zI~AN#wkq&>y?^t`6l3xSQGU-~s&mWJP*a-46SiO|78t%Y9$@L>6*K`lTc9Q^AzAaQ zz@Pe{f8+e-=)^BWZZwLo;HC$ipArwzuRgQ3_w(W5dFwGRo|4CIq7jCaKpQ|A_-rWV z+aAj4e-U41k5k`3(iynxf7TPFV?sjKVh#MTZP1z&ZU99g2jZjKbHUm)vnCKw?A>z^la}s0&ijpe`kh_N!jn`}t|MejHG=Wl~ ztW#(XwMMWQbcZUue|>&jHWfD%4+=#P!DDUwlSr(xut?55nkEdtb+|MwpbDVz!UUAY z3F_CW_COqZZP!}! zo$Oj5k+Yo5bS?!&U62cS0*n0P-J1mxsK1)(o_3#ipF`rOZR1xWTVDLs(d8H@irI0^ zR=XAK*mI?a7<52$V|1tVqp7=CO|ACUiLIY1JENzg zQMB#*V_n!i|N9_4YAOjYa?vUz_DCU=pm;T}pRncqUj=wokAoK-UEIk#Z58>?l=AXXpj1i6UAm1P+06vQR~EAH+JH06wow zuo%|xItc?ug;1>l>_ua7%-^_qO?RA-@`~<<=i|zw$CxOxPLlHCG<%Xrmqf!lc&P&+ z$cUl2m6(O(p5A+^$@x7w#6R1YW#%?;oy-I14E(_GZAb9nI>f@udMg6GIgLPtfnv!F zF+%zwux`G8?RNep3j|JQKV=ICdfBM`T$RbIhg`|kFy+MN#J~VYG@&?_9K{YoUx1)x zQmL-MsXfO&l8CZbT(Fi+GMRllf}n#`4J&-^DG6gOW&v~=${QuTN^I2O`gfkELIK!s$i!i%-Ffc-|L|P2BwW_ z>7!t)X-KI4MvtirFY(>q%)fNn@;YC$skL%_Eu(xay&2CZf2^p6zg?q1XR}sIUU5T=I-0;A}w?P}s{?gIY&QJeE z31-B8#lXm|)g)hT=#zo%9^Q#xDRRICi0_;{z$@jVN2L||Td;3EtYLh9&&@GOsgMr~ zm_Z$>Ef`9(P6OT>f351W!R^hso3>2HU=L zxa*_1=?@gu1nsDW3l82W`}{<6u|Rw#?#V}L#?ij{f?yJ1B=Qag@j1F#+p;C6C4t8V zeCJI^Hr`QEHWwDT0&?+*q;im(8 ziGx7~zlebp`%D_%_!T4K^mLEJE$92$&zllkz5HsL9kXEvLlPr2V5=h>$0A+Bi1Ol{ zGD^Ra9MaJqygFdEIAGNN6GJ)C*V1xqvX;(S>t>CJ z$-pMggf+GbP&Ra!W@0zGCdwE3uIG*Q`%}3`4esuAwtRVV{qnB;fI9!mbNU400)c84 zmyXg9K(;A*H(qwY!vEPkhs@n*D9v4gm~-v$^?JJ*oj$N*2?UaSp%%gymGlGqL$*g2 z<*Mp&EKrZP460+l8*Zk_zqruV%~P~zhp8RF;@d9v8Vo`)j6{f;4NZ2rPX2}(cGiGr zWx5d+5sTcO)^RcOCq#6ZGmdWfbe{QP%E>E`nD}aVv*m*5ga}y`IQ)0kfhB?2Xl2K; zU4yP1nWM8sk&^G(x(-d7sKY(n_&E=6U~Dn-R;uF!aePuKDUZ;CDz?yWk+d|p%3QV0Jos*+RAUoD-HLs5?_D z6`>iyPLQOX3427tTADdf*j9(eljmBM9)y@V4?+4r(36f!K*)b`3{+&eF2u{3Fh9&0 zwo*Yw?7V{~_sz;3|GaQq`ldhE-Gm;v=;J&tpryn=6tTY5=`WJ@`I-E%=+>?Mrl9MK z#SW=ttKo$is=-p~Ey8Q?To1$YVf@`tL?8^)h%C5Pk7>f6IMAM>c!lnyBqmr)VXbv} z;Vd_>ImBX~o}Lmk#u=ic8TsSJ*YCr#C3 ztNQ(Mff$Hg>I_uh3G4rfqck12Cj8hcLCj1fNdytu88A25`#=E#fh6uy#T09)CU5Ga zt|o5Ol1C=gaUNq3@`DvZ71o;hX2(1C7gx9%U=K+z)_>hW-U($n z2_T440~m>mowL_(Q$v@+rAiVrDpP*W&LfKORs(OMHyPA0%mrGb9VYa1`CNwgtSrfu!dKR=7<1zI;w@&yJyc=2v+#U#=c|+2TvP8W(K^&fD0V1T_SH# zdO5{p4nDw}eUm3GueU41{P;Qxo6k+Q=nNDm-Qc)>HOJuw4oq1<&6=E%C(aaP1ttc% zOpxyzMd&Btg1E3C*0|RvWWmw#NPB1Et|9+DL$Qf-f;TvpE^0`!CFw?4j+Q=cXkKA| zztyVXM9U1{`8JiG2N=|&ZLJ9+=9~Ca(h>G&6)oyYHq}TuIYZqH?J%zm&Wc5HRDGT0>@DxZIcKI0-pY`by^>Ny7>#oY>0VTm~31qqzOg{xeD8@jVx{V8? zZ#_*=TkF*g^Or->XWMqTF!Gcc7Nd#{t-&?$Gb0|~@yOY3rh_3U`T`&_=nSKF5X($| zEq*kTzf)EYBnSoagSM!r;&}#tesmM-$eJ8i6EIgSokY$R6kvBvCU%*}ZH;e+T~>0C zz=&hWI}tR&@zHF>2~zAQyzu>leOqCU?iPhwF?&}PO~M+&*u6FjbwL*b(H+I;u*i+c zU3;99^h~r^GeIs-zK++OMul?NGk7iX_Ny!H-2_zlnOE!f*ws(1QADZs10sa1Eo%0v z&C>C>=ODq1WT*lQJo?w!ETOCMuCPT)Egcjoh_sr&s0B#Ofhl*P!ULdc2nX6jRMcR8 z!I&oT;XkVM)8G%W8UVwDkcFqDGj_O^7PVq`P2We2zJyZQ?+>h4O=k!BIfI zLaCK};+rNLjx5d9(6114AVDJl6C1)xW;JXlL(eT_g#82pFHET@BtCG8?o(XS6Aqug zb`5o{-joa3QA2R3V+on;n^JY%fm_gZh4@@&vmznnep?Qo3btO-g(%w^QLrf3gFR5j z-@BlwL`An9RvSBrR#(FiDX2dI_K~-ewTxMgOfFEN`3tT*pT%+JZ^vnK6-d=Q zP{?kkA!bArNt^@5trVQ?kTorS-^J;c(xpbFey)CWU8?#!_yfY<2=TNgGU$JzofCEo z1^Zrvc(8}5IX^Ow`mw@J!2naz!iLp<-WN;o8$2Qz?gsiR((kbcbPA;W`U&)=cqeXm*PJ?eCdfQZ zc6RS(m;7SsvyFha=>xMuaw!daYivC`^PXg#d1l?jU>Dn%it)`v4_IE?#oir<+4*1K1|%ENRc~^D!}f)p0f#4mAf@QJVQj;xPuj zhx4YX+lLv8ujz|ONtF8oa@_5!7Cm28Gx8Erap5T#xv0HXj@hgxjg|^h*g@5aJY6h;0|FG!E7c~qf z-Lp@_gECd{n?7_HxG0Y=J5N9kIu*07mLud|)@=V;6kc|BeEVqYO`XQe-ovui^G=)=TH?VOB7(IT-1<)*_!viD4_JoZ1e_YHHRkVXr-K| zR0o}K$aJf_?|nMn7j1tLsDUny74J9DkNmp%QytxH{=1rg)^+E}?#tnI!*605VOsMH zzENSz05RBd%X;9nUGqDAS+8DjP~#bbaK43t44XC~mIahT-8iYt`-^Zo@1x$})jzEu zI7iA-cgiH6m~%Kud?BpYx}T^|yrcy0U-iv)4F3LYR^zLS!^<&g3=aLmnQ(Sq(MPuG zb@u}U%?{Ui&R$(b7eF#xL$Rzih;}{(pN_jtg2$&tx(ah1!ladzhwkGo}W)Q*4*n zF#D@B2q*~WIL-XQ`qeGf&wuQ8a!AwtHX`kS&rG;SurWgHU2*M-lzN+(k*e8ofR}p6 zFluwTQ4q)+c5!|hY?3sQCt)cY<5rR({O){!Pn zkGv@kGZp7LBY=E7qZX9R$kJxu5Q^QIsjC{^&->+TXYGnR)=m8@ZlB3Y38$;*B~3!R zwXE!{bPXFIVZH+CdCo^>u;S(~=0c(RtyGg4s6Rh@%k^RH1W2Hm5QTwTtA z4z`=~x+}oS${Z~{RtR(RVZL<^@`mRlLy3GqEb8y&7(s&SY9FXhtkYE?ifr*Sva+ap&uMU+Gt&w#J1_Yy(ImPb^=i0fE*Xs z2fabJ&eO+Jv!s60rlspxgp5ZwE*X^;HKZC}%bpj_u*{|)Y91!ZLQLI>EG9N5jLd@6 zIXe2G9QJeu>)R|uy?#E7aA5-U^#n#FH=N;HCU}fy?w=f#@S6u3{bJsIW7~`#8uTW+uyG6m{X*JqYkfjR(F*g zC#-qrYu#@hV2FAl5W?B{Z-p8sI-RQguK6Xg9*m^rY0`nD&(3y$Y9vxP-t`=~5l?b) zaC|Ab(36SZWO=P>3Du94FuXYyuK*X@du@DB(y(yW|0O z(?85Pij#b3{k*XAEiD7q4~rp2igOKiIKbrv70kx>z%N;MZd53xB5kFWcBxS z%2{@0$?N{j@Z94wV&+Lwj^=YAXQwqRy^PtJOl{}8-VzS^HQ$Fa9neQ)wLawtjTvps zdgVN?RXe+wOyDM$p^;EIQhik0gY@$ zu$UcLlrySUaflOQJ|su)kQRo~Xpxf~TBX6*!z-2ls*2Cw2CPX4nXQM_f9AOhznF%N`H*!aDdbF@J;h3wf%;-HsAYrHH~(ANfP zi)9bY$6MoYg2HgLL?Dp^NSp5h3}r-J%%ADG2QR8l_|Y#Qe(@vl$luuLdx@PFTE`_G zzn+WIN`9MJ2Uq5e#e;Vos4FrpM=_OCSrQx)S>awafs0ct}AKqQ2l ze3J<#12@5|LgKT_os$RB>!3s` zW2VB>Q3cf=JQ(!JZji4t7Xs25)8zmOU??@m4+8VjobeoneOTy}S8+?b-GErz-u! zZ8Mvt>KXO?T>YiXS!9*nLXYqJ`Og0(Q#R>#C9F799MWnnWA)iIeqN%e$D##+&;LoR zl?jA!IO#Nu2|5Y)oSV1v(e8H}ART+T-o3yN_7q;k84X<8SaMmu$c10KeaZVF+|QTM zj*;`l1Um^ZQ!EUMCrze`lJaWy=H>fBKLi<83%C_hK=vlg{M-N1u_y24P07be!4>v} z*nx+}uns^DvP{Sq1f#{M=~>zNQn9N8QJApSwoEx_zTzs{DQ{^4uuCvZD{xFZ(tfud zJ}T4bB{1d0dUZ5?51r?V33x>qOO^Db*?mFxQxdAuu2m&EK`W!HXO%w;PXQ$>sWn>^ zbhZM&2cr5jucFdlH;2W|*zJI(cO}8NeKCYmcsEj=xG{zNVD5b%Ub515d;qXm2>9I| z&kcMWz|3=M5+H*@sFY@`<Ugy{D0IH!tJ9;5eM6aC{3uYuVip#5{&>8j zNT%esL*tSjl2v@@`q<+|QvIU8LTLO&-+CrX!Vt9IkCp|T!E{7S#tRK7wls-YSbtLk z2n`vxmZcpJuUG5C{d*#cq;gvsfa0k-KG87ZFOd_7By<*J1}{VC{Z(Vz7ARB7kUEW( zhnGIxl*~yU46g9{ak(^Ig>O&yec^=|4NSy|X$8E1juW=~x3dSL_v76!^c`1BI!;bcm)`>)@;mQ<$-)+=tMEo%Vxoc7kxasn04NZM5flI% z4@7Pc4&i_-s+cM=*=RD}xIv46H+v+fy16B3lA>a#y!1}fC~1;5LFPyCyJr}ruj@M3 z>5ua65WM$dMV|Um$_CGwOy1I49JA2D`B_*{kySKP&ADKO3l&CbC1+Lh3_Sj40ka>i zrPsllQ*xtUTLNHq)?VSF8&xByR5a*Mr2ZhAvr9~v~E7J&eTaz;5u(#md=Awp3ANjH*i2j)%-DPxS~?n#j$6Et`6&R5Q|In?ayL(1t%87PUeOPkO8r?_gh zO6$bI++D~@FJd&~v?^1FWng`xF_awP#h`j(SqBk0tA|3&CfZ`^zZu}{(xm&>edMvS zZB8tj+(*+{&xOgaOT{WT63H0;?=!paui5X|Uw_u%PLRt3lIW^s3%pH}4UWAiS-$r^ z<^8^o@MF1wf~#Ef{8=i$T-?4~>py@H!}t4iXQNtySZu58PxQiy~{a%l5 zKAs!5T`!^(f~6BkBkc-`K^>GUxApkn+POSr&9XJ``zqy>jR|w9c8z5fLe-9j=L^-01_t#O1_Q{#^ZZNha|R~ zpiOH=64V0qfRZ@i%<_+p1q~rc_Idx;o3hbOO*#F$7yHr|7;zib_WALGoqv3-q-hCo;&)#UQaL|(6Uj<4>v-^t>N*cm|wM*5V_f*;g8zVmAkJ^>Pda4 zeusJ|5ZQR+l9bI}3xY!>qC7=JGW11GQfDCF%uP<9Q$E_$-H3!TH;IMo`-z;@^cZjA zBM|){BWL6xWhtk|AA=H83NiscCZVo9{@b@W-fzV!KfxxruJ-5YsydF^__fZX8bMUn z@A3WHwDGwZsQhs3x}8ssTz=S1bR|^1hap z)f|1c&@mE|Nhu#M3^-9Hz`uPg`oVX~ZaD>imhC6H0FuV;Ln$%kDYxi55KAQD)|h=6 z6)%le^(q;RGI~}s83y_!)>{l~gKG#$I~2()Mw(7mZ;HY7+>wzh;CJeg$fm~D>^7hU{r#D5+6??}PZ z$CYxA>Iz~|Q^4k<<}m%qp6_G#D}=#8`0TT>{lL{WY=h@@|0G-^FDjhxld1xCRk%^V*%B-dr`{1ivAeUGLRUnqevXAHMILoKH zcH`FP?S9A#Cvzl(m_tZHZSC`CKUksyYD*i>=O*4x`733b)k!eCT*_W?Dt8ekiY@do z%P?l7X783C51)5RDRmdONG#>Y&BAQpZb%VGX6u=C%FJ@hfUntuwc`zR_*ScD(o&Rj zD{d^~oI2T$i3N!eb^m}7Ekb$^vB~ddz#hGy0wcn7(18#Y4a5M8`~r1jdsXWDxUE1! z#nkC*%c~)#h6EOCEXmLcpgIx%bUQ~qjTeHvhDkN7Ol8eV{h(c(o`fv01|uil$UQ^z1BY1n8G=UGG^ zbmsKlY72X+O&n{vq`fAoXX#?*6y$vAZ9i=9a)jYcyvnYtNSf)wFc}Oq z;8_GQ!pIAt3k?_-2p@uEolFZTcY1Yzr6yY-m9q>2%tzx$M{-y!WuA0;Tnt*gr4qTi zE;MlDjkjyzG<3NIwb2#43{NO_I23~V7I7siSxvjRJ)j2153%+ERXJ8~IN3Av)Pxx^ zIO$M6EXY8~YV{8E5}zGN9W3JWN-NuP43Hr=lC%j-=0CIi1FEv0U?4|;8i)`Bpw2E> z(@LTy1rJL44)&(g^y1KNCtHrjgK2S_Q^&H{-XI== z*2*y+p=t^+CJ{u=Au^xR^m@FqX6j#WuEAXvD~0y~lS%$lm)YDNuxPyb(Jj02dDVAd z#f>&5g4?A}uK(DM?NZgA*_5)fC`Hde#BHp}4C;}9X#e7==C2~ttEh>uxH{1%=PMP{ zoXW~Qmg8=QLqSA`Hf5gQJHCITroI10F~c7H4*d5^8YF-u38q7qYu2eLP2- zkdOobi4mlIsCiVYrXXDbSsR!ZL_yW*u19abfE(zLrec!P zPgb*i1aOFZFf}^}UOs{;ar`{u3X&&TGP6gj_Z4g}(y^Ly2B82W7oO=`FN~A}qN4M| z5_}b9;?Y!}n_mh$!?QS30^46Wg@H1{U3nGsa~DD;sM=DlA-qH^?rUMwD`v~It}5Tc ztviKATgIV%RkJGZ>az2B7Vu0}s;swYeodzn(ui83ijxq5?kApzX6k_I21&PxA*`N9 z%Qv3dv$_jjmP~1-;S-9dqef9Sf^DbYRT4Q8K_my^76I`AmmWI`Ctc$zi6n%5C8z-I z^bht#eMSj|6np82A33Hp@un=Q!4$J0V2K#=n%ZvTEHNrEK}I*YQ-@pdPIV>2nUZb2`e=ftIx)IO!Q!|p4z-d z%^;vqFIv>lz)WJD8lFdrAxISxz3;GeX02gbsub11z43OLs?Q}2J4qW{0W{~nc&?3U zTFe9!78D*_Jg8f$!i5yW994!G?r%yN84EbD8W^|y-M*Tkmz-9vzO|~ku#8q`J3pr@ zVpir@=;;$)SXiGpbJ#DGAXY%&P_rcfCc{ZsGgni@<7(GjW>;KTu<$FKlA%h;ig_cj z|HX0wBdk&JrgsCah$di)2C5Jw3nCujcuMxQ@IsP|Qm{ImZuBZHK_=eVTPhHJz!Q@l z=W@p0q@;;~a&Vdn4XiLpM)W>}B5L|T5E9dRJ!2}W#XKCh14Q*8dHXy_5k-tCnDn>k zDg_y)yjn_)4I2lDVLFM7K@cZh$~@1n)Ku_>c>toGkz4BVD$pU&>uEqdmYtNHtba0z z>i+>@K%T$ZL~=@sm@JL}O459ZS|Gy_t=${0lf(AROoW{#$QGa~1%<#?iG+j%7#BiC z5)vo;B%P3{oxnJxazj&VPgPU5(H6+yOSmFbXDTpSi)1j(Vu{#nF(*@zEip)C=FBV@ zf^aSmSEImEqgH#F(HzILp60gRH4W{~V3AyH=I|AO5OkKHO7Dgwm{LPI0~IRVqeR_c zq=84vV0V7BGf~z zOYkmRz)H_ns(p5M1I?rm*u~>w1nI}MjK>pBRW;T(v~TR~A08b$I5~Ol%B34$-CSCF z`uEpAKl|a;?LXZe*mW>mR1a#);HWqpFv%7dU|m-v2m#Nk27;$9XoSrO=AbHw@YLM zBtYa+o!R84nUI#CXnStFxUy>P`m*ZANO7_xRa0Kq($v23@WgNLF5G*({B-)pjco(F zic0DjTNr{+h>Hs$LN28kI_CGM6bdRU3r00Lg^8N{c*-3tv3XWD*wBH)q$L1Q3amRmSB0t^80yvPLKyCmD@^8`fX$WX4Mg&z<>jgO+Bfze zJUV%JVq$Lo>j#TVGjlh4KN>1uz0MysX2#91Pf{z zcOm1BbZ-9O*tv7#r%n#;-7_+FWXG<3{X2*E96CC4YyRZ<^E-Esbo6dl<4%D9h4?C^ zk|M~U#ag7reT=QpV92HEaDKS5;k^x|Wyy~2O`EoU*f%hE<+IOU{J8SN%atErzFK_p zWa7kGueXTBma^F}5S`gv;&N4IWuloJxmbu|r~_6yL}H^@Ou$MrqOv+1;g*)}-VX;m zd-`__?;k%t+1jxsUe=yh*ofgS7EAWduON-mRG3#%x29=B`-c8)TaO(XyE;Af;Li`A zK3~57@cYHZ#ecqev;5*OGk=(!Jbku)-6mgntx!yfg$4^5FlZT@*%1X1qPgq+OA9{laE&)@#%?b7nI6Q|GhZX0xaig0x{;~4Y>z^MzUS59we0t`~=5^bg?lO(WYapX)jfbLR zCQDu>62GKz5Lg|-vYIMbNAup z-_N?drCB@`@I*de!{w@lB0>Svgx>3RMeF}Zae003cLdzv%V8S(x&;msgkn^WT5J zUR-GDXe&Hf5@ZVJaB$v+NhAaS1UyXZu*Jv6rseKGdak_ca(#WzK<~ut%*fcY2P2Q) zEPq)4`qjJWo%eb^fBQW3a`s|vUF6o(U`Mi8<|30tK0XM+kAbmNJXEbx*h50L;Y_k6 zDj_>Bzw%N|Yg?<^GrO>~G&uB+nYow$`t#4#_4U@S&e|(=Kb@&iD8ewzfg+Na@c4}6 z%pYsHPsXxrqo72kdjUla+oq6 zTcQxjH(TsES$oURmRHwYyj)vzyQ%T{*yyw8qy2+@!^4kPKYf~*o$lyr@9gO;DlOlc zn&*m47Kuz+RcNp+%4BvL4ECh>UAt4$vv;SQKV5jgzw^O^-l35P^WK-sYwPZpbM6zx9Ti7(WP_{tEgGbv$L{^5dyc?a`yvdhbgdwbhG zp2@j|SEFwCqmhTRbCYkEyxs-R>jk_l)7`yY#ieIMU5N-ofxrrxG*qjPV==WDTcMU3 zoz~5JGg7Wzsv7L?8GbU-*3&-W9$)#my8h*}XMS>J`OUOvqNC$>TSv?O!-Z0%1IHo! zScxQdr6x?R35|(PN{mlBn49-YL;c;BTlYGC@9b%tm>T=z+t;@%%kCHMCr<}gSC(Ec zE%pxdU%hh86|oBdWw^MJ0@##59zd7yxW-UNqAfTwd(XbBf4y?&-ksmOJI1FcSJqZn z*4M_S$LGABmDTs}KD=?yJb(4dJ>`Dd@Js#alF}e!7>}=^P!W8!BvOk)9;{V4Qg&oi zS5^OgU|`%m@y};Ni*LOvpH{q!3ol;Ht*w8=-+4CrXl!D5YI?Mx;aYxSNow{gddI;T&%8Y@9FI9!>4&~Z2ZZWZ(rVgSQ^J`H#LS2s-^AL zsZ%F1()KA;F1sULF3`Cgk(=#Ko6#H_7nPi{BVpUttc--SW%-vcpDz0;|72mV-DZ$U z_-rNz0N4<~BLyI&00x=FV1j&&NN3mCBSRvhBO~@^rtis0-I)}1G(Y`9#nIa8qLShh zrDdf@jvtHJmY`Gx34~^)24`;@je-JH6oI%b7{$TmU`|11X0};x2)6{~q$d^?9;mAP z@#@vHLl3)W@N+Xa?R`Dd+#wE9b8<7cZwYhR zY@rMW7D$5GEIA$s7UN+t#VC`fk?X_k;pvGxi;wJYsIR#5x4Qd%%^uJ5cOMo$t$moD z9K6+7bNa_TXQ&m1PzaPT5jBa3BMk}1W`Zw>03wYvSSK}Zi;mm1BkAzr+@h1m%8E}` zp82t>wQ=ZiUw6lyw&q{&{#Mu1)>L=C)8F${?)RAC^I9T))6sVs=jVZvOg)NC-gBEl0AlhW8+0RZA`iBf4Og~9;= z0fNW^16dn4_!9{rL=z&g1f=m;kcj>N@KCluqckgI29O2=G!UdSP&S{#5phK(3Ly3; zGRT1(7*;?GDUrf5862rt2zL*6y19{1* zDLS1FpCge3`x7BtI&f4blX*lEhZM->^YwDM6-AW{hLDR&)lwC%qiU(lq|*fH^j2F) zOkAQ;t~99BQBLP(vq_E#VHyeoLKv2DI9i1!l7$++-$24?Ml0U(cwY*KjV9x;^&+0d zVU97XZFZgIY;n=y?5xlrZOrBnr^T8W=}L+S3D#Rd8nS64!FLlSfB`#F4 z+Xc`>Y=%-SFsX3O(wGc73m=n!R8WA5^$M$8s0%jOqh0ZF;c;RvFG#7E^VKYfA4p>R z5D*^%lS~y01U8Y#%Hb#hItJ44=Bm`HP`uQ9uF)Bmu|4H@YZLpx z1H=y|1K%RP&=$5OPa@mm@6Y<~FTOa4GGG;jjIkgwhKST+wNYizD>WuTtd&mdiESf@AmPl~Mp9nS!&W z0mUp_o)z~>1W{9IBK!v+Kun_xH*O^2@$sb*{9uSKWHU8VNhqHm%;o4=Yz@JmNhC1? zNH86e(E%0y=P1hngEE3YK&G%LG#(B%2qdIYIe~#}gstNU%mT^(IIgngHjXR$1rdSS z_nw};XWwVA4ln>gkOV-QCMAm6=tYtxJCfqWE;&h+ojfGZ$z#4Ym#*p;RAIXBa_%|j zZj0hr*FCIz#La9gA*4?8m0_2X8aVD8Mrkkpb0L>BjfFFj`qwRps*}9np-)P#V?>pUgqlNwBHM&s=ZCaSnK3tj9cO@C;G~ zEb~a_H52(wah!y$XQ!UssA?{Xz99NT%A{#mmJ-_<>sl&uwjzh3l!$Vw;Z|02&^XT< zoosGCefYfUwh|{NPLX<(#2XPv2RMpSIo5Q{3C9{ok64z`9jle301k{i!X?BL0`e!< zrddH$ReioXzOU=UxY`o4#9D|||L*!-NJc<+aFyj9yU{L8_Ia_h-KnbQ2qs$aH8m}& zt7?3=IDWT2ef!1jFZJYB5J2OS?drzF`QS+41xP=y%gd*CpD(}sv+MQL5b=zD(%;%v zJERbFQdy1J#SFaAvnW{y;o3AumOb_S6~dKaO*C8$1_DLN@Te&Gt~HMQK8n@= zzthcE=ZE(+gBeDHrjxMGtLwD5fNKq0B*FOd;y#Fbmc<;W3xa`h-t}KMv%9Q11VOi0 z?Aq}L=@EP@NkEv5ZcLNxa((jAghp_tu0`GS3%E*>i6Gj5I&3ycwoQ|B0q`t&KxLLy z14Ei-0~J%%)OF@{bK!+^meUa`i{i4Z4jx$|W9<4Tl9F@03-FU=Z!i#t16ky-z+qXm zUAu*GGmQ#JQc<#_0Rj`EX@#zjHEn|Qk>@S3F_J{E3bk%DL)xM_!ih1|YBc$>TRk!D z8ZzZsOso+gkQ&GorPGYV^F&A0qtmz_!d2W{6nEaExZZeG~dqj?sN@;zy?{YGk!k)ZF#gWqX@dS2}JSr-dbDclG0} z|H^ijj@Qdd&ay_CuAe;qi)FOKf#P^`Li!|J*Tt!6G~nKcN0KT!_s@TH-5JLd*Xs;> z>XKDaUiZgulVmS&MC3iwC}A{M24R#CuZz+{lwKC~<0w9*D2$CF&1OM1XL&=ELd#qE z$ywH1jAyqOHy@Xq=YG^h*+~#>%J!;gZ@lnCGbS=>7}kYh*HFQ6gr-c_p4s-yFxxaa z$*Tj+7!NQoGMmhb(X|_IBrS!Xi`n_NZ+=e7a}H{#=avmnZ(%2b)~IR(`uL|0zr6hB zBS6alT5}h^zaiu}PIhU21!#|gS=*fHdPLJ2^ujbt&+p>=Fz#NK^;5^5_shFxbbwqQ zBv^|rs|8|-<1I`g#5pH8$+4`YvQhx#G`#_PPy7z1gXP>P z-1^>f@JOHr;6LiJd>qF+9ksHQ@~j8jL=<)q4@SAH#0p9!B|>^-*$cyh%1oZW{Aqvn zjp?)ydEg%tcR}2x=PnVd;VV#8jABIr+Kd#f(#?6ddi(Xe|D0WZQuP{&5G5p- zt#Oh!e=2H`s3ypmnf5Y@FRRhLsFuJ97?r;-6ov2if!{k06l7*P zb6^sO)P6E^qu%m6Ki#JFx#M>v-V~!Jv;K9G?}56*cwsng5G)F?I_1{(T5Q*#ysrDe zQ53CGG?L^H;1>jamYfg*YU_Ze07Zk21GlSsgYDi6_EEY|v-7OjLzjfOrh!~yMULP?YA8yg16~D&LeuN0IDkc2E+1Eu9S8f7S(~JDqII))PSUOI zcV)eQ|L&iE{rCS&GO^roUSEtS&!&@Si^Z$|Q(R`fZKG!t{s);!WXU2WQR2Su6h%@Z zwNMhNeX}IX@)B=ZoY=`s>`B}-Y1$4ZNed)JFvVO1XweR)MbV=7MKQPiU40GcLKhPG zE$2PwIflDPY;q#!?=HXAjqdH+U?V&znW1YXXTIA1Rd@6#2!>ZL-ib!ctIeRb8}{$F zh7aS(MKpV+m9`ims8vRP{?k8JN6%m+aa=FpCQiDd+^Gb2Rec0A0vQda0b>QWL$ghi zX>xpQmln1+&*imbGEWgj8s?Ys3#3akRrrWvOVc~;_*m0gp4)fITLk4QTGw>viZvwp zdJea9`68?qs0jcGzKe3?mKP#l2T-(~h@ec8FnO*Fy2_w3LAop#06x*2&q<-AwIPCG ziV=hZdQWdM0$&rQM$w%P*VmL3CevW%7MLVS*}!HXzg*sg#1VitDHv_~i!ZS|2?FGi z$83&^Rdon_qUr-h>+w>P;sQZw0o>;EU|eNMX{%O0msM1;^7gww>P85rkxY_>f(3Cb z@Ku)efVHw&S(X}-RO7fZET$T+8@&_Fn>zKiq{R&9mz*(8my;V*dQ-#;F2ghwd8DZ` z40AWrGDCTl${ffCSQ_@Vk-!soa9AG8woBf!9^QqDk%roWN+_xjg%UgjL9{&Xsz$$D zT>;bTYQ1Pz5fn!CVe)d??HH zdVT5D790dQ|x)s+x7fIAQP-$@^b6u5NJ9BOUZ)g(>`=1FWBpEU6m>euf9@CBg31S zm3gJPua{;(7puYP_aA@UKDq)vhw?%|Nx_Sn(JMMr%bp?`0m}#s<7abvD#hgsDu8;q zwra#Dwdjx&J(k%%moFq^te6w{pgCi?b@u+lUz^ct6XAhBDY{G3om%ar6@L}f z4{s<=X%GI**Ka;|Cl?f3xqtt9Z~qczTu~dX!AWQSw7a<6IsW$a z$q&)s8Bi!Mc$yV4q7M`YxycA^G-qXUI+_u|Fq0{|P=M%nuzZQ2eUfX)#c9xcAMi?-lVY>9>`gDj=nTzhi3CnkfhczwE>yKHb%V+QFM?Eh;DkIMAZZ@M zATA9^H~5VtMUFdz0(PxY3~%JL z36gF$<9&wrVZW-{EP7M7x~g?f?8>%fb$7PU-+lTUh8u81_2@Ld8T{jT{3sedD%bZ2 z$~Fv$q$z?50N4!cgTR8u(b4()>D~*R_A;oz@)eA*k*tzJI4~L2nlw6(j~@Q694tJ4 z-sl|Af|o)ligg`-cJT1k+fRS_<=fBmgXgl@4uk&1SKnIYaS{}lwb=W* z)xVpk4cDK{_b+$uznrWuy0gcA^FTIZN%H^s@1Ng%_le?cfJ4{6+u3^2o;)*(i!>s` z9nGir9$mcs;!7y4CC#2UM(5+h*R!Kvm!o?aT`4)ezyIq$=TF{jrg>C9P98xP#|oW;&}RWw7x!k^6~W9 z_p|jcBssc$o0u=2I^G=3sGx9o1)KoH#BBFv%RYivGV?nb39z!t~fR>0$GU{fae`BPnIphRc1h(1%u{2=s1=7x--5<3uRf4 zJJYAl!FeS-o-D4s>YkuR>ti_VN z$dZgfHefS&2sQ&|2nmoGhDi-YC6m6}2%NMh|uY_!AJU(mp?-Hy7 z^i`-(Vm%`z%hd%=r4gF@+c;! z$sEa+ReL8_Jg9D+7`a}5xVpT0XV^WObBT;b3)PJ13tFSGef;?O$4X-_j`C^J?oF@b5ets>zvfmb9;JWT5c~Ui>I$Xd>+rA1j7uGDOm1I zHHQpe1&0cSIEWNYY07f#*6l#zSf zJO0)w%^~VT5pW;=?GL~DtregqK@?19j1wNmH!AfNxGn5aDk;|MGexR^Hr@gUjIgZK z&HJZb{j}uY%TU#H%GUKhk#>R`NH~-Uh3IgY0ha_QLJ%1)YCJmwnuT}!a#+fO*)R%+ ziZjO228@>C^yq9g!&YTuENGBpTQrpyIG1H|FhUuKP_n`@ZTJynji4$Jvx$VzX&qM@ zvp^6DZ;}XRh{|@W|FAcE;&)e$S%3BIr^U&2lGKX+e0lcn;N;EL;Bt0!GhMw@ok1kZ z7}gG`E&+v0sBon;*HW}G%!8=4f)K({n#$xYXNMJP6j$cNCdF1Itz&t+oaFOT-Ent$ zp)5&2_zT@#y)_pZ^WL33o_R zj$(}4{fn=zetP-+KZZxoV04kBSg4FFZ<55#I3fqb^k!Tj$UKM%PUJ955+old?MRG9 zlcu2bi~j2T>iv&@`_Ih}|MXgSqll7~>TnfF+=4F!Eta7wvfLG=E(lH_n1X1s3v-rl zB~TS42)-SZwFb~m=OF>62N&bn{Zef&SDN$6fDzlAv;|c%I($Vkl~`Oh^LvWfjVDCN zQY>kPH@RSt#0j&}I>yuL#;x$}-$YQj0kNgK`-0lTDK`+}k)#7w6$xiHZp9OE89_7z z)xe=h(nT+a0&i>kUpVyC(>h zy~Xvx-J9**2Zr0_gaXCp)1;G18M@w~DH}zyK!&QZS8aUN*}2|Zz9DF^51!{rR&D@j zlt2YEsiZ*}jIrrVfPMwJKHNR)@7!YdIK(4H*$_14jBeZxvk3^Q8ov;N?!>aPx$*GKnmvPu&{CChC7>97AX+}%)! zO{N?{1}2}d*%q4A0rjCi9Cr)`7e{zeY8J}#Qhhl-xV-w^$CLBlXSI%P51xJV9=s1r z>4rTlR_0azxLi9Cq@4)FLY(DT7eEVoEf@lRwkpk&?a8BZ^B4$?q)e|kL{K4)WMQ;m z2~L0ead!7jckhZ38n-uy4Bge-!k)uqSA+dj7KSgP@|ifcr1&PF2jS*_KwysuiyTH!HKeE9Jg@XRP-&~ z8R+?mU0ylWlT5ZoXWV9E!3!QvXd>jiSeKQyBtcSDl9(C}Cj)_aFo*>LSR_J+BTQtI zK@mBL8Z=eNkQRz?l2q61o>$xV>ht#S-1eqfx#@blPN75d2Aefm-jSrLDEo%l&*%42 zfVHT|GdYs75(o!?DrBn+TSYMt2Pez2wLlT~%$1 zQVrlL0d5$}{vRGJj;VlOa9oojb3oU@FmgK(OQlrX8N1%1*|}Jqe-8|1=*_c-ubt8i z;tN5=R7y+Z7RS}{?ryuk4sCLgFe7H$kczQHHV{g|FM*1J)Ua}65=K2O!RoPW0YxPM zIgHR#8HPN=$Cj&#bYeTxd_5hw4F>_Ea%(#i2^T^ z8)lWZpluB%7ouFx3dNn#y1V-To{(G(BdjEDDB5<;n-~2x5EpQfX71#?h2t(t^*f5b z6N?)#d+``Q>>nQy``*^@v1W||LBg@NeSZ}BAIW9b+cthh@ozAR zB})`ZQQS$9;w~lM8*By>8l5U^nvr)o>(hBtx#iU3?L=Yj2@DEpTSn^+1%M$q# zNBeQs&v~7_*%zXcQ;hOx=h^euf4F-0(|C4g=e8uJn6j&j-Lu*3#x;bnsZdG z*k7OT%uW|iZI%!Qz43 z45y!OE{3~zM`z#W3!BTstAG9bzkY5rf?BfDhfdMx9KHJR#An!Jy(|zE;_6HMT=RI=spW zd=~m0%eV+4MWdi8TT#lX%n%j>NLY%O%w#K9p4#c5W;9j31J*rRUrx8qQB;B3kfj_X zX)LO-v?YldmPzLFBSp@HS4oZm=E-PgGnpQ_U?5gY;GJ5ze|Ps0>`~GB_2xyReV$6K zmrB!IVMCOw0O6KaYu2_wR)Js)!%Uhk>Bghk;`_bjC(|B=LLyWKNOqD?9y~-J2B{FD z;n9>R)}V0la+VR?7^$zU;Qu$bgjOvyW>EU{WIta&@JlmUZIr4z>B5MLC++N@y?#30 zx;lCG-R;|--hBG`{Q5VFUQx9&U-3O_7z~o(5ECQ7Q4^9}ipGoxra+XCgcj!= zUa$?fJenL{-+ecoKcnC!gkyct9$td$+HNnC9UFE(N?LKHBuH7Balq1LrKuae?%-^= zerY-52&(arED3P+PF$+TNH2ohk*JO0Cg7zY`XE(;P(UvvrDbx%`SNCY_PRZs&yTLp zuirU-ThR)c!iMXQ7RR^i(`USxWmwa3%7=$9s?`nn7DGEE>49OgOg$NA(N87i>#==dFW{aC{e*rcH z1IZV+^Z9K7=`hhZGxCD043SzO5D_g$5yS;*5e za6+Sf;CVfPPs9C4V$Ml6F;ol%DX>$L&;>3f^67Axiy&+i6C@#x<6!K32oV4$Os#0B zzG)N{rI^jHzx(x%XV1PNDMt{By550IuQV1wup(zFate>C;jkczc>o?wX=zF`odFIn zVv)G1=loO~2n%)-~8~GOmS0?igCFrN@d;Z`Gv>b z@yp5VeY&)xYNf&OX}!PnvSZ5~07#>l9ix25*@X7-FhD$9A%bBsuJ%-WLy#L$!i0yC zs8$;LQX&gHS*mPYK7aeiKmYCO?o+wD?-i!1(ORBIg@T~+hYhG5egt*!mz7mpWT=4-Rw=qWgrs#Ru3U(0eO z8a1O30jPqb8qH+%uuvaqwp_?ZLK7S zoQ)jL%Grm*8O{v%o#IZ4izr!}Wy_Q-S(YWQQIg8>5+`+=w7G~Apg^0ey6shg00p|B z1$x)N)K>xK`onzRd){-Nvls>p7GtR!*ZnuHFJHg5JU@qksv)Cwxe#OR#>&k!6bFPc zv;t1*F}bKD+I9*^d$%)w-0U4}kMI2R-~T&0`y4u>7H?$B8*X+DE^a2*STT>2s>pk{ zcArP0$;E}BZ#fhSMuP|kP6$*9G^?mtSRKMB3Jy^R}tFOO#{i_#mzqQkC zPAX*zd+F>>qjR!*_|!$}JX$xzCm zq47f$>${i7XTSdV?T?M&QLB6F z{P`Ej?9_5PhS?Nid74QA6@woddKV|P0CKf)WZ2f!zG;pWr5TAhNI)dy(r;i9Ih|QrG(RtsX1Ou!CR4(rKK*? z1vNZma#62Y+Zpr^Ab$y5q9`4BfMQaDS{KwBtln^RxHdhOm7*j;P}WUrMb*lJkTMbt z4tfpa*2@!^BZANYe-sqOP_PI#fvJII20YENYuQX23-J=~3?_GvAH6(&^3Kj~vaxEI zO7HDG{o6nOTh0#wAdvts@L5%EsA^YMTNthZW*B;Tb^6e8+d+f@IR%zz6yYO;8YOj8 z%X6Fs#x4p4-*WUyUGvZ;M2a*qM2Qdyi2jt%%Guf|pw>c^8wC zu9nEAi!Ie`p|}a5KruFgYJs4{^F>u_L&J@-Szl0@TL=Sm5tL(CeL*V8Qen1#F&OR} zR^835)qBT-jVHUa7iZ7jO>RB*3X_e^lRy6TUvak&bOcr=Yjr0*H0_D1t?KasMccYj zZFUY6tqK|lg$xWegFziWmMz4@8ps%&e{qQfjbXSBzaeSObYZ)_da5U2F1hjOuu|I* z#C(5vcQm~xLrWrcn)RRosY)3MNI-*dERI_QmLP&Mq&(Qg(jsG;4c#aqkYav8OZ56T zpOsoO)9Ht4myB4g&YqKMMj40UvW_>(m$#DcSXCe&lc1F8WO<1XgWy}M!?5| zhX9OGlnGg>sUH5z*(@I`iee7< ze7trSn&XY@zL1|IF%u2KHDeemh6%A=Y8{?l2zmym5?*mL?sVbg*7PLbIf&^sRn0$r z{A&O30?3M@z&FZnYC7F`w6ps>m6#)*3j6~gHcKF@<59b$OqnK(Yn_gjTXUC>90N)NrOoC8# zt==1)#>DFKGH|L#PDZb8(49LK+cq6V9~On58ql z@#Nt4{m)N7{mtp8-yA-AW2FXII4;WhT63>Ez7GO0%qp~9hRsCjE2BpIVhE8#0Re2P zl3zECl^fUmON%6qNf_jBP{c4GLoF67-aWcRG5w>Df=f#*aI$9fEN2Y!x^aU*F)2(L z5!&Elo@Uo;{ez9!Wq19VYz^haidUFcyEB?i+4dlv-}3S|GsRuQ=}}zLhloC(fCgm( zRS-W1t~kGdgAyW?8ID>gsRwaMw`zvB!bncKJXzbjsI>Ph&As;O(Mku(R2ld=LdIpO zEJL};2e4b`AWCXm|>W$B}M9UwHQMAMG z#h)q81Py}$0-qvTip%oPZzZbrYUlqHm)mpN=oyFqgCsV#0fRuCg@hy! zLP8wG`5+|DU~w=34A_8;?KrmMQ`U~-L*g8~Np_n};Q43#zL*{LZrFjfFUGYVJ%i1(ISF$I4S zNmJO(R$X*1nd-Pb8HY1Pc}pZyptvd%Y(jdMlTJoc&5}HEd&N|KWube}Sv%`Yo-~H% ziQ)zwki&faaR1fD_7&z#I><~aH;!e7F7__rtiiBfrLqH!(rCcEU~@|Ayw(AOsv$vO ze34WuoNkDP_UiV<+Ro+v>8sw_ak@BmxU&|72cSZbDU->VQw#0m@}0@Bh|WPROmY~b=ppt2eWN6>&4!ZlhH!$Y(;A5AU_*$#LzTn@wh zLatwwHwx8pDAtN+dI@pKEnklrQA?4V^yk9)x5)G;YoKcoa#x{ z`#@F7#eQ>f0`eTOhjA)H(%EFX(_A{tRyIQs8N_BXS*ICsPKnOUSX3$u!IEyK;^kz$ z-ZMXsQ64cEgu1~Vp!SXNa=MHti#lE=D8WOV%ju2gK_>ED8#K>${B@GrL=3+Kp8j`&w%qA zjb7H11?$*q4@nD~tNU+u&wgL)odETailfoSlatHew%XgLPv5skhY_h&>mLAO;xj{k z7_6AHw5!eF!dtyZs2PiVE66$#+36zLX% z@n$Gd-?)E%a`lc+$*DrO+yc|R9utST%7c3QaZ(%^t*qI~IB5wblOC?j2Ah<(!SWr# zU9#Dtu#o}1il89`PwGs5Gm@|(Q9BmX>S+*ktT$^m(-33_trdvW%8k8d?_nyxijjhg zN>m$@Sh|j2{!p^r9-SUM{xI2l6&AWoAZNoEf(R`2_i1m!VrK|iK*{u%*u*2KEg+;C zfj|LpqYk1V2(YCG1SV|Y_;5WVE#X9TdP;Az0bBOrh5qeH9b?W}AO9*na!& z2ftrH5PxamK~dflq_Hp9(m*8WTcCJnk6x9^!`V3iY#ysUMtiH#)N)?l&6GAA82|M0 z_3`Ob^}G$tT0$JPR?d&F{_x=Zdp@-S8#$Ym`SVXdPbNni9i>oMw2+(On!(5ba|pAu zW}|_sR9Dlvp;BdsVUNk|1A<1n#Xz{l@pY#&cK-D3*T4B=Fjz5~SeIM!hZ-qyFkC-9 zeg08y9XLn{MdOsa0O_0xh5q(!6}YBaU4z3!aC_p zA~zJ}&Gzu5F*sm@xxwneLT4vY7{>D>E_&C=<}oVm@)Z)^vcGs^G#dSKk;Pz&Lu`aLdTWquza>P=7#uVb=Z=16#th7na7NS8kl{)&%xdTHfWK(5fN!mOJ(ex@FP`6AKKsxce-%$G zvR(;>ajV(wB;&xg=H{##jRVC(nQT9k>4w9yMuSdI>wxu`OkS&vL+n0+j3KBWMOZ0Q z_j2*MIT)~gdvv_E|FY5B3nx1WmNpnT0_VTG`Pk`hX>@L-3d693!RXfj_k>&k=WJH5 zl9(Hc&HtTx;=S{DN!uYoOHmSd-dl1)r$`#3ASgX**-1S_7a&M2tz+#Gn;v} z8iQe?RNk;7!P!}h)f^;09 zV3k0FE7{EX-2Zyd7Mb#jwojLf!0#1Y(|3%yc2j>qd~M9J0L?}u+m;W**p8)XzNP8 zyVdVb{`E(2O(qDUWbbiwvKPpsLU%vh13CcQ?FA#5MD)(~o z;rjl~`q8_^;p1|7^&kKG_0wPeZLkLBAzH1aGkGg%eaJ-kx_b)E5$M+wX+dGZr z!%BNQDs^hzo%Y~x@A&n_>yPnFH&+^-J^%jt-RG^NuNT?}!Pp}15^z#<(Pan**@%HL zH<*AQMV+oJ%M~5O|0yoB=C;i%4F7^QvaF3H2(AE#g~Ubx1OaZ~PJuf`iXtgW*1k)o zEnBi;E3zzaiY+^K;w5eyyXmCcOzTcNozA2eo?do()2sfQp4@PVE6j)QJ@0v*j9)0C zPL<^f98tduLgbbe0JyEI#*s4PVqU|yqR zID)Vc3AbCqiKyMqkB$;ntDp46y&e_TJw-(+I!ZG!LCQ-?1*mM?LO7f(0s7{l2~rTH zlpd|Mx`VmpJ94Pxqrz@X!3iCl&T1i?C7wWy8nT5gW({F$Gw@-il^q*gU{PF1a z(~p1p$E#ocF`DT|)BRAamoYc`OZN`%zF*$?is2@CHr4J9V2DtsggOL*ihw*S(b?AQ zam45X)HSCU1tBYkYML<})G7dzyi~5W2G@?hW<^8Qvf)TM5G>ZF_owGhw8WAZ%MfHn z(c5CE4(=uelaL44tqxSNAyBr;gvC!YsX}p;<+Dz=1U%4NJgLs?NqSuXNtALPj3fPF zFA;RP{S+Mv@CjDPa>1;mngKqII{d7dTG>7>6z72bIMMXDWmq3Br_A$3w<~C|_@D$J zT24=pW(_&g&Xsm~smAzIoIl0|eo{+GHth&MK1R}XOi{}Lt^oZ880iGX7;756#9RE5`L$slRk!WLmjG%SboD%wmU%!9w_1o*m59gP*&YwIxJih4l_dh><(_KED zUAe!q^XX1TOyS%?DLA0gzm{ zrl-0vhO2u|_Kx0mSI;=!IJ|lK=G|}W?Ja^y@=DFePEuT@*|~M?_PcWXI!35xXV0rs zt5$>^0CPZ$zaOWpi0Hv$B$KO60f*d@mC{Um>&ohJeR^Ov);ML-OGE=gJ{T@fHrD-O z&fyBcxfnLa@+p!DgMg4sLeZ-;3rCON{<$@KEf_4Qk*3=%LsOv$huV3>MuRexs%tEl z5dd+v(9! z$LNRyN(`|>q+vlDekxh1t@;?97m{H}a&1M9HdraEXjK!CH4s5jpO4fTb~2e+PUhF; za0|mUuSW`LRffxqStyrVWf%jdDUxUj!2*VBb~IqI5HzI$6(DvBBSIu0%bb?dbD3m~ z=VKwssFb_S&SIt6O=YKKZ8DUo8ij7LKG$2mnky}IIvdB2-_EYzfiEe`Ifl|OuYkGe zRAh2{=Qv-U2YyyVQ;Aql)0?7H@e;7{35l0>clQaO#srF5e9o-ytn5ABx^X_gaS}0R zL^k^D$%}vg_kUa0?@?SnX|CrQ`<;!8v*&-*LyMG^biXeBA)=3q& z8=YGRC*P%VbBRdx#f$f+PrtJu9O#L|t+2egy!qhvqYp<-|LM*Dl*ymxux3zmp(l?|M>S$ z9G{`sk`nF1+tv7$orBkh556~R1Fu_sbpGz)vtPuFsc{QAI*!Ay!y)2WEWlPqMkwgC z>wCxd?p{EF3=LU^KC}G%XV#%1x5F7x^?9!+)W0&AnqCJj6&2{cEJj2BL=g-@NQ{i(bP{yNM<#JHfjU`P&I7_#I)lxdPZoCWH|Foeva2>b<8^bgJn8m? zFif}F{?Bol^|qDPVf+&?)JdFU`|Rhu%h~sRw~u2xZnh?AlBR8%rf3?b4AX_qP%2Rb zqq*V&F%pOyU@qqZB)$Njjen4QW9wMwegDhz{2p%%c~%G=duP6SZaNFth^{?@BT0%6 z)gG}fq(w;zv6@BRlA9Qceu_Hue$yujQb#HvZN`KCJJVp z;K9l})+~SnTP-1}B72HxgFQ9fnU@@MeAM>OW~&!jadLU}tD9H9y?=76Y5977^yclS z#qzO6nrZ)}?Ju+HK@cyJY(+>0N!AaRRI5Q!z`1}Gi}JGRuA}(SwwlDR_V1scKe~kn z+V#tJ_444+hlBI?{pzajADdREBlq^@`=9^vlOHSq1=H+U(}BL4&|k4*ogL94TK)M? zfB(zhet;}9*ie~~%Sx5ztoz;D+2SUT=Rf}IKi~iLAMDuJ7)b2Ib-n$az1Qb2zvFpN zU=7!TJ|0q%f@pvxLGgk8P9J>XQsQd&HU-b?N_gV-_9?>^nN{iTu*PXwj+w3Nt3{BMc$Nn zo8m$zwPW^0aSFLiMU6qb*jq;K{CNNR`sRa#Qm_?Fa!Cw1kbPG7%zUx9U?&zc#LSR@ zN+UIen%H({*)XU7lUHr*vc$;pGC?d?4i1rPF|qQ>w> zj;|0}`rZK?iD%U;Z1eQ~1agxQ0FZE8C`lP)D_Kri)=9Fv4b_c4FCUi0k)cCVIy=Bu_Kk6Xgq6*oM~&hWps|WOSDc9toia$w>Vc6 zh^ToOFC4dvf_a(l1a2*Iy6-f7|3Fn>tu#s^81E>81JB_)ENk+dqiMA)#Tcao=TIdi zd7)D{Mgf6Bk;A>yFT!NcFj~XxFv@|KTzi^Ca~;P#OSiWq*iDUOn;YVAQ|89lB(`Tp zU2%WCcnW*ExhZcCHAQJPYen^rkisxR3qs4@X;toSZYsLDD5}fT%Wtl3zrB3%5h4bK zaiSSU9!1`rJD?EmC!n>{@`e&0-QC@qj|OPt0}s498{#j5Nf z+1F`q7*jtubj&%Hqde%p`SRl*zyGI75>a+BP81L>CDAc_B95gK28#feETo}8RuZrb z7!^qh9IGp%oA9Qo{gK+8g=`lU9;aRn433+;-*wDk`eL$M1f`6@W1aqpHTtmMrro zlHDTTkY*wa5ypplBGprZkKiUdK+1;g&pR2%zuAfE#TiBu4^kDbOzzVG+~= z#>{_s@hT4IvS67;O>jj?4jTb|91SIAi|0mIM=>*$nV|~tkg8dguZk45yC+IE81P_) z8yj~lJz_9m2|J;_n|n@1i9a5xkQQxw9>x1bao9{xqxe2M(L@3KRM{9XGLH#{2Dy%v z?X55R{v=IT1V@snO(wFynL3?QoKqamyOV>nSHlr#g*4h0#@m99ELRVTbm~}1RqwcA zE8IzaeX{>#2^8ekSaoOZVVo`8ni-&2phZG0I z=jz$(Acio~0L2C2UelcI?p_z=siLMlXPRbV+a-)T%*kZp0WKoRrK%?i+L#gfv8<^s zlHjN|%L2xfQMBi|cS0da{{KVP5onHOA5`^=C^}M63P1?06j+TJ@{(Xd%rVU_iFX{k z8IQ>32FH!m=TBa}d-ttQGj@zcR#$l^up77(l0AXbnJtkZ-!?&Qe1S8F;uy%WsHYPj zDTp7lR7UU>Hqmu@+nP$E4>BkTHuPxl(-FH(*b!!64DPYp17X5YRSImah`cFq%e-B_ zfA6UU5?Qadm!C-vv9s^g_NR&i?-%%A;Uw{3vC|46! z7X=rS)G()(v(ogA9jlpNet7e{AO8JcK?y$l$=2ous|aRbS4q@XW&b~p%k?*k>kh+z zL5uD6?s|9bbLO^lzwGSJ%8h^S;l|Qj=?|Z_F*MQ9uia zDERN-w1OgHOh~9o!qSu36g12UU9E_*6m&eARN#Fc#MGE>>p6y20LcSf5IM_F^~#b% zlW+%6QsgVD z)1+wIrBZbou<(mIiK~Dz|Nk`aBq~vaYj4M+5s}0UiT43%AW@b9NOIFrfNVPe3&g*X99<6kvk#?re5HU&W{_ygtKAWiTv(sjv> zF-D9vC03_!7Dq_<{np} zM5(kD@5!h8%2~%sRC`AZ%kK!o843%%`o(}HO%hduZCLBBaO?NOSVYp5vL@$gOi_hI z#wq2zN+MoHBOD+!j)q;MM_{nDbhNi3kswJ#c}>i4w1I>vNO-gZk}nHPiUU55XzguS zdu!Cv@({>i0D~OD+LZ*?waOBc#3M>Lz)&bWS+P)nh=f@RgW;*75ul2SCJJ#)sOH>} zkpWrN0348Mbq$!%G=`mSi$qz9gdJ+@nw?F%GZHqG4HF7Xdyor-IJgp9x4op5h#Ouq zt;Ag0s7OKzY7Y{kR6*}5Rr>~a+jh2G9F%2;V^kh!Bo+$>h?bTpPMAEO4Fwsvb$FD4 z_B+^9pPHOX7#2hDNQB{-IJAOvs#DecnCMokqse61w#%B9MG=7}WXO!j>y~1xqQlY} ztZY>-s#405&A1q=_=&Wgv{cSRmCmL7gq7l0EzlB* zhENsqIvG*Y;Siy z974lEf~BCa`UzF{v{+hYd<~3sC-yb6qgAu)fJUcrcs##zdjHPm+^4@-fAQtDr_b)* zx%2k?>y<)UvUEihGYs$<$}>c#;yc4tXM89-)}PNNzM(^Bg zbLZ;S{#qR{EQT_00qt!tujII06U8FT_CQOU%rHgQ9jKJM3Mq~zSOU;EFch=aUEVmh zy1PE&x!&B|o>D2#GBJ`A48vCxADWv4bBTsQAj7Nd`8|aSEW%Dt>q@YT&C1%Lv5A3P ztza&Ur{#{BeHZ!zTxBmfvkB7(>`rdCpTLt%JZ;(9a6Xt&+y zPBd%o(PnCEvGUW?Q}^Cq_~h#0zyJByFP?mS{oKOJfw6jDXHqqIN??c>iA%DW)?%JO z0E-1(31cXmsJhBTk2AYFzo%AQXx2`@d0=C8^6XnPA6!_za&}{5=?C>{PsvXubcrPh zXkR2MMM5mZDH7sgq6S(b9YKsn7=RE7QK-09y{DM5G)ETti<#b1A)oXMPC=mMw$=zd zuq47k*|IqzB}4~3YUMTi!fM~CrM|Q4BX_T@KKSs5w|}z#qx1E@es=2H=QsX%f9uA( ztBZT<72k*B0~W!^NLV4GWsM%sF%t!Brb-`gCQcl#9Gfq#A1M8JY2fVI;MG%O*UmKV zT%7pZ7dziPzxlgcE1Qe?E{7$e2oj-W!BPYxMllwH*IjyfSXv*~wpWUGFHQgaoyPUk zLwB~vpMHAs+ZQ*V{&wTp!}Wju>GFU7^W=*Mn;)E+esiI(Gn*D@cv_XTk*s*;P(hs; zw3lCVrpipS%GV3R%&@n=kvY&vpIGSmaQooPuOB}9%l%({^v?F?fu;FTTNltsltei| z8^Ivf*3wQ#Ix;F%)alMR$SBc_iZ;qdbHLp@;w+6j*U!&i-(KF?Jow4aj(_#}jrT5} z80hwz6OBF1#o0rfcCwqp5KjU*jT!d1mC^Xcqy1Y;U8|$|&heh7pIvdlSgq~9!6@l!_-Ewbka&MNKJ-Jy~QxYH$maxgH>@lnY zisFDGpbR1)pdx}gBI1AoP94NyL_nl6sIApHYESL-oSsfkf9v&6_}=gP-uHR(^aCv^ zJwa#pVe7r|!<)JR2w5zEnS4%|5{OWX_h#6@hHPVfE?u6YtO-zEbqV*sZJX#lG|^u_aOQ|rtK)Me zOaseBfQy9vCQ(T&cs!dJZ*|WPSIiBUuiQHN=5hajKK-!0dU5$i)z9Cz{jzm=dZgx{ zpQagugmJAxj{|@j;=5I%Y#aY@5_mL|=q)$Qp3hmCKDv0d`p!W9qsgi_kIsC4aplAF zq0PmkTMG@F_nP{e(xSCrMv#FJRybG07V2?DltP|mg3cC`!*zzSX4~BP^yf3df9za% zz1s6|vV3#4=IxXA*GsL-<3%eI1?w{v-;WhH7t`^yjvFBmupyW$&P#L_cyxKOvgRD+ z&CaCd$+ElWGw$?fZ9hJ{zHsvX<`sbcD6F?FuiKGB`FI95C}khOBB@zbAgZawVUS?>Ax zYVyaKV^!I%bYGIq?hy(In$pE+WJw0FBuZ4D3|%`BzjizHe713Zv|#pP{>tr!4_m`) zvrP-51wTy%|F+S$IbAz*%609ir796)T15t-VP8e=`rRI3*nT4t={InK9!YbKy8E!> z%GdrU-?hGbcK$CboiCS8y<9%MIu%;IUi$E&|8ldfK24rz08BCwBH+s;N(4~pRraKq z_&l%4Ps5pJL7fj8YjfWpEnd1_^>Fgg)495xmD8`DbgzBauy(!Z)r0!?>mAz<8t;DN z9jViIRXO8K3O*Z=qE-p4RbYghR>f(7M7^lmN3`V=U58YIbu^R4i$Aum&(*C?hyMBQ z*1ta7n;k5eyAu$f306#;-is+616a|lc#h0YZLk^qK@aA@;yYn1ykcNmkr~HO{sD`+zJGH18g%vE2OU; z^KRU2TA6CT+jrnbN6KKmw=T^Pa4?%HzyS;rk_^f0-NixpFwSS`!N^37xX?{iq*_Z8 zO^w;MGnG*Tjfq1o=>sk4)m~-LLsog!0jI`*;RGm=0w}}$GSnbNj0&7qN_EjjlS?gk zl9Ie=Z7{_WN~Q+t<7WCYp3PLgd)oEmjnJ*m)LZSI-V*9m7TH}y=eux9%tClVxzwyy zc|ft2(LjI;qFlBP>g~q4=L_vqy_IzVSBhOn zVkDPG3V9L{iw$$wYDjD&C2)y(tBc~bB$ULfhZLK=+>*MvyzUKru)p|niR4p=)QTrzO^i|G1q>q$a%=8 zO|p_^g@h3t1faNFC67k|ph=3+7-})=V|9c^CJ<5(=upa?YH5rH&5hH9((O$L;_Gu< z<*9UIp1O)Gl2O&0ol$JC{Axk$&Y!TtkLZk8uGG3r9#EM-GW~pon5ILd1chGzO1R zYe85^8eUm~yf#(Ul10t+=YM!{<=xiEPj{N;$b=K@c)CMg0K+wJ@X> z@=!UVOrWD_8RArcaT?SqhhmjjDoy!}WQGZ;O*5V-Nj&Vgc_}2C#LZHf0z@G`5*aSp zw+Gt&dxii-Bxb+_fP}-5@dXx=bm_G*dVQRhvdIw(2HE9k3?N=n4rY*7Z4LnC!)FufdB z%TT3^Kww-b#`lMd_U;w%cmyu9K!|pK1jOawQdpzHDOzWBm|PHtV?g0JJr!kSI!!GH zF&T(5LcwD}j7f1=$o?>3-#$PfG#D*eW?L2_F%fcF0x4kus>1aEkC|ymDN!fKq^BhW zyiq;_sUcxZiRq{>3Ipug9Tpw|@q`9g5`*K>3V9R){GZ`6Yi{GX!ss7Jv=9U_0A}B( zd)A(P!vHhbcM>2#P?RW>A}PtDqLL%qk}KuP;-vE8x8x~5HvK#`)yqBi-2TGGajRKx zL&*;9EOk4X8xP}PT}3yiov$~;$MfW;SN(4{>xWf&Sq1xEGEZWG;%ha6rZfPu1|c2Q zD4aJnFsRDw`S@ns+qeK`mb{4dF_K4)@NrZA=hw&o{`1ejynT7Qn3k^NsER78x{--u zB8YLDvV-J&x&5+O{V*slT^QY7-u(LQA5gS#y-eB7-Ex#q=jEaw_g&2sTAE0^O&m*) zI1Clbr4$uXq$digWA?a~pwM{ME6s-3Zb}RVIL4BAA1bM?W~yM(O-7;tM;aV$nlM+y z2&#qat>Sb?(+J02str{k)Rj06W*%DM#sR$H0dzK)No}U%)*!psa)vqv@L7NPp# zX1xBCbuOZKx7mM8i@7A*8c2h1Ih@@0hOYsP71;@#e7AkoB*o)8(`*|iT7}tV5MP+q zM1!Sc_4kL5C|WdWPg47`+LP5T!v?YtK{=pW(s7NXT81j+wlxD_fHlPnG0O(FS>b0% zt8KMNr%gJt(h0(OlCA=OTy$2JQP^gQoRK6Y_>*Z*9W-t8VEB+1 zB9@CJr6WlhO*@k4LzsBpz;Sw>H!X@?man5=3?<8kPKXO@_XvWaNm&#^*WDz&dkIvn zHaI@!j%o}BLsABAuwhuI)zX?xO_noV%@qzRtP&W(xR1RQ3Zg43sR8?K)H6a7NRuNm{<@PHv~C-zU8nqS_^C1hn*Y^T9MKymXxG zi{5wD@FB0>5Tt)pS8H{dVZs&xpFU}v)J5c-btmtu$=fhFV0vrMS(b9zP3!4Xx?YzQ zt@NXdPVXU%4>TLMNUwgPku6|=48weUL_L2>)N3r&0*Vv`XyTeh^Ms|Eg33Fp=qb3| zG_E44*#zBN2D6RkLr}-FFY)HkXeQETA(|U}<|}H<-uCW^?(O77@cEXjW0N zk@bp`I=tL?sVuD7u5A;tJCAM;WSjIp1b=j9~|maAB%9Bsf&`VU+F9_CMTx_`3{>lcq*7E;PrSkjkLc%n>gp zNjTr0KYG!r8?AJAErE%uFKzcgF<3h}!v!r8wkg2#2%)j#?KLo=NsFN@7tKsG<;B$T z*X87{n!W?p3Rp{CE?6Pft&s*Ro)0W@z;Gc+da~Aq_C$tbf(nl7Km#3yH3>pySyzy9 z(_TPhY?u>SErC8V?O70?x7rTJXM$WQU@XWzU`%AKte+?+b;bABradBQgJzLtja6%^ z8WS(x+U{J@I-;CPTE9v8Bo_GwTSR)8SH%2xZvwS3oxc3W&?O-{} zN{?luMgyp@pJaz5z2JqEXhM<>puM!*Z5S*B4q+^xJ)?;eg=#^Dc0{Q|GXc*>EQ^Us zS|>8rnx-UCx(l*PH@R@)3k8m9N3xDib!T4m4qtxyeX+e~ExH74l57O67SOnA3bkXb$4$b|H?>_1PY9npI?{L56k`6`T5V~^g+@( zhBHFJ+OQX@KE!-l(bo1?FJ63k|M_>vUmOz(EyQ_sJ6``-O}+s0jFU=M%muXodc|^x z<57dOasT3Hp)BVr7;s`I&JJ1cr3_O+O>q%*cZ-rkHGLlySC}z|@pt=AQL@G7CE5m7 zqGry3wRXaT?o0(S;8;5h=6-aBRm|~NfSDWa^76Jjxa;+ATW#dHr^w%1XoJ^c#Jt5s z$ArlWdDeZc>BEzTA}YD=%$(@l4)?mVpjaP+?WY$ao*G(Z`%76LMa8x0o%z`d-P281x@nRaOAn)^Y7-l6!t?^4h!=8R$ozPR2QenFK|q%Ck-zQ^-au>BZtG0~;9XJw zr?^a))5y*+d<9ADDG4EqML@IDE6@wQ(KJf~-86dx&5k655JD@X&9N3I*4Q4~naLzI zlQ^kV{$#02rIH)SrRBKl+`u{C`#tY-oD3k_SI;(|J==P|7I1iyM7&P1Z5UIxHetIB z!>~LIRv6;IaeH}>PmGaugs3US!<;&$xVk?g!bF})wxO_q5DFHrfDmHwvM6`8`eB@{ z*j!PJ(tV*cjq{V*8xSkw+Umx7N0}=$W8NrxL zuADw@%f&ief-hKzF{Z37 z&)<~V$Eo5h&i6qk!9^uXkc z%SHfp#i>5Ujd*!a&hJ41lAu%}yT>FafDfhCRcm}N7xpR61fmtCYc6jBixqIHPVsHf z3pj1sTrBuuB-lo8cpVB$b|;uJOC_3V4ZMJf(wzXT*&I~9u+Ix^tBr8`WT%Vw1~RlT z;j?p`?m9imlx&`@zHz#lHA~pCi2-mS0HMYC?)mQ#taSVG)4%@vKczTXx1w848lxIo z|F$!KTkgH0gr0qyl9e%7jm;Ot+Ij#Ard&S2RXwYou*sdR7qM+;9K-Wqr>vVkz^*kb zy6K3+AVT6!wtVr6=Pru{@<5c+op5fmAd?<%((Yupp(MrklesA<=odE3`NdC?zF=C& z?0fwwKlow6IRKL=)7EO2o$0S5(JYs2rq$g-d8XCpQf?4Wl)+=-l%^S{U}_)$5kgCd zqkMUludW!O53af8ia{X(Us*#wN06UQ2yK!x2)Yr$^(fn+ zrCyXS4aZ;4&%UAfI^a(bNdptsTNmxw6P+5dLapCl{_!vW*4s0upA8e0oPJvA{L&cS zDdl}%kh5+@f^a$zNqd590LwW2JW5uK;r)32p;$XZ$ttK$y}5#;C2u%QFr8v!#mWFdwBo5zrX+SpOe$~G2R5WKvLDufBFx@H!Yi}+bhS}p{$bCk(qnlDsb z-#{d>@$m2iO=~~@*~W&&=L<>+ai`Y1Kfe7buOHg%u~(0O;KY{0MLB@o6Ad>ICrAW$ zVWmC2d3SvE=jrJulG08Vud2oj7#GQuxumI956Z?#rE@N+J6Kf7tCM2=*dIY3KMuMY1O;{u7R0#~0c--XLb)9W7QC@A+bifcp&Ac(L@8>!eR{uLa8WK!Kng5S5dl8iepaR5z|A+ z&p}1u0Onw*ga{!NAYQ&|$lnpjL03sb1CKT6G^dm-W z+qVg=dQj_Lk-W(WCPwN}rW!)>$y6Vt8pd*=NP4(?bN}sM7dPK`rVnak#qb6UOD89< zpFaG}?qZ;T$V+CedtL2bm(7)KUI9tFyZ>Q0TzS20C?b_QtM2rvJ$|jXUN(kLFj3ps z#KUmTLXMI$WN*;ODT)Bb=d6@l9@>uQP(#hi`Bno33;FtcxUhtpvb!2 zjz9m(0rVBYwa}N;DZxm==A;42=Eol;xd(g|@(Umdpd16CEU0Nu2I*Ndd^6KTVhKbmJ@`8F(z$Y%glPOB;~7ARA+=S@o#ezROikGo?jRnMC8I zTya7sshpWkh9TZ)9a%FHB`_XLM*FGd3_bG(J9abaG{3Z4C-Y zi4>JtZ`;@zhW|sxiDFx_D2bHBU8E>d6nBcN7Sh^RYqPvW@@~ajoJ^LcNhV1Lm;k}d zv4lf`RM?=wt! zytC?!E=k%koypbxmy5fv^OKL`<@<8$oXs0|H}9U_d`_eU02ENVlI5F5d7+n%S$T>O z6~h?5{NazX*#pyZ(b>E4{b_;s>)Co7}V%TI&-7dTs%oH--Uuv{mv&h^q+ zwSAjm>vYz9`S$1j>kVr6Pt$b+LR(AcmKKz|iTd_iq zq}=;gUoLJxXL8jTD8rbeyL*+!xoXV|aytgH7*QPWKAY}8Q;i-cH?xJ7r1qW4^y2FC z!Qyo^#^tzrZ+tsBdMDa*l5dmwc7~~4-+pqOehi>#rc`bmIsT!n_4CpI&$TxJF|~a_dgF$J{4O(}pp`F5wO@JYA zquxAY*a}YQwf<^w@FviI_vqv7=*QaVKErQ=X|XwY3S&hmE#?Xy!FUW;Gpw=c?vhL? z4GW^^E1Kt2hN``jS9*B1Of!aI4S1>kIE>K;Y?+7i`JFa<@E>0 zn>~04CJ|G07lwZ-8Z$xLQMG=}-}~*`KYsf9*GOChAO#_OUY+;%-yc8!b8G&dq`mX= zSAY5Yzf0a;IL2d456uoRrtkNj@2=}FW`AqGi#nm$^dzgwKW z(#*lm=xIUh5ZP)b=f@yr6JWREY;bbfKg;E7Ilj(H9h7og-PLgRLNRvJsEy*5?amaf ziv&}XCwld?=pD=U0mU|g5y#@3W=&IY@$otu-l7n~r3)jqbgnoHgm6-@;&DP3O$4pG8Kp5?5!GfAwj(i;Vok*vd+oEq{7q~8A`DOfK(egU9Gs^~3rO%WP{PS-rE%nU zmR{=^CoBxJvTP$*f3ywi^g*J?->zi=Ejl9&boq_L9 zk59fYnq43vvW1b)#-X!)p}YHevCVS+(em}>?H7b}0+2$Ojnmtl&?lK@ z3N3?4T~S)y(FIJHiL@0@nc*nE8D@x#?NsN(`5lxNaNMM`RYC6e#?J<$D;Tpk!r9Pc zEXR1G@iP>&zWXj63K2xc-)`R+&VtM~5|AkhtzZB2x69Sr2M=Ov57Uvjo63Ie1LgJJy z%Jo`fj~8o?LfBde-`rv$#K;SsREkFtE|)JG<#B&<)tTOE?ifh0C~h-cGrW}zP!Qxo z)W)5i`|0uzv@nhUa_DhJlG>uykAhra(a_@n7x@%YV#QHjn+BCTZXFrha+_PCUE*mJRMuBB|AkC|s`2 z?m00P#J=t2)h=!>Mv80Vfb;Jl=i0C*w zv%OVk_!d~g2~Ve|pZl{%CptrM8^>)~$>B!as8H>N<)4p$8Fs(S&p&GJtXvfp?Sv6h zN=Q|I&PZ(xdRFPBoy+dzwW#K%GaRqpNBO0nZiDuDYj6QY2GY{(vDZAWH*8rQtJcc& zP9<$hacuw|Q2Tg#ll89%uF0zdoNBQG{LkFBUXsQa1lgv1Qlv^zJf@{C6S;r)87Qy zl_<3p-rp6syNd^c@xXebG33+})f}7lLeYkGR5z_&D?jt1dE8oC!HQtJwR(u*@yYO& z-#o3MGDSNK)2ySeC=KAa7!jhtkmdH>{ckiIA0ARDYLQH0d&|xKYq9@k1(yfMa==S)Qw)6Z$vq!ITvig2p!W|#;u(jZZI+=NT<`g=#5{( zZR*}yvKCd;qv%xEXRwT-LQUz;PVdL34-DS{7Gn9lj@lS%zIgVuUcOM|*swaytmsdk zuP>f}&}w!2=vb)M42kb5V)x(&wo=g~Z3tO9UVU(bHIBRGvP?70R{qQliXRS$<0Bf! zEO=qZnYrP%H+c%OJ3oCcX*tC>vX)9p8!V~VQ$dCF=r!mJ_6tXfVR|dSNV~Vq>;~`# z8j#>xC>hldjpJjI_MqK{%g4#-M>pE(*1}KD6my^&ozdiGx_(db5y^!lAF0mx_2VB; zU;lmj{BL%$!w4r%caUi$*Q(V`jBG)61@ZRo{`d9yyP#PV`zK!Np|}Tv0S=M%i5tOd zzn|~Fu|h|YTf@<17%zAU;0;tANZQDDH?Cg@Qv0~f3sBa^#PAk|zk-&pVJ5{y{gWS| zN{)`Oqa(D6NHqjdGo!>V!KHAmJR3Hnm1Peqh@#m`E7-!Y5VfyKHmM;7NNX~=PCEro z>QI_>+-LY+)B>p6!?jirtz4CYa3MyNts8m%&?MHD@tqG+P#qbGuje{)< zDMm$=x(7mSrrTz^!*F-K`}pSbKeN$GI4D8tgG$R~o@c!v7=z{;4Vx3XsCDB*SE=DDiHi^?qRhw{ZyL8M}D^fe(jb^uy#t5R9%hKT?M^mjZ z+56F!ApAO_fR`n?Lz7{xs)Dr*XC(beCK1#+FLr?Z2{I(f$N;DNh3WrEq`W$3fxbu= zB8X-az&4J{BxPiW7g2s^1sjaEYp6hTFtZFay(39oz&yWs9;TNt49?CzNyZdu7>d?` zC_9~qy9>DM=s{Qf$ z{p|d&P*0}2f^Z}07E(6}%Ew3tZJ0r_9~D0ji-*bPQ)}>!a7 zl@1PQfo~7Sx02Q&z$`>SQL&~EIWcd5ND!SMtn>YS-rqI|%kmca@GeR(E$38_#^tJZ za6lfHSWWF79Z|f{Y~~lU&Bssw_}~83?_PWgxj|v-WOf_G0Q1maU<3nphvAM4B*P@- ziXbbQD0Pv#TdrsahfJlW!mm7^=vqtQ3|&uDJ%{2$>Q<#9eE)spyYG-{Mdn#J=MLJC zYY5Ns2~u|&Xb8iIZo=f4l#T^~3k5!?R_M|pT{;#82|846G&Guyb#vuMSCT%*sJMaq zk~(C?9H(Q5EX`arvlnUqwG|ZzW*;7LIPTMIQ;;)G&TYSlvb|#`F8#1b zx*J)`AW3l?xz;fBOMpq54#B!jd!}k*j)w}+gxs%IWzU~!`f;_Y!*T(BAgzFA!Ha3V zu3$ALj*GN+)gb-9y=hb`3PFJ5=`p?)UZucmP;SmWGm$k_`)9 zzQ0cq4VxlE5p|D_I1G~*#)a^e)B+hLAi8Xnc6b534<{|%Dq6|a{N(xM@_W#`ZmEqYG=9k}1e}UIUnKzLfC@N^O=SGXDyLDQSj7!a&C~AykM-pvC zOKg8?`(=0d4ho~+-B|4lCqDP%6IlVz3{>+t&9-+RUv3}&47>Lf-9&On(+4ac0t2@? zH-2>KhF8h(!Sc>2rbUsq@8-JR!Ek|LUEQ40bW29VDBVQK1?0PK^d{3C(t0!-!PZ#r zNxt|toPD*UHQc-73`fO#H<}TQXGiCYtKV-P{{;J*uAUA17&^}J1Ul@Fpd;Nn9hYD4 z9{>6GZ~wl!f9YhmEaP|Di@Ohhj3zsZalt46t9Y^N1t+>XLSnm)OU>qdzWk!wQ&|PO zfVoAgJ{9FLoC59?pe4^Aip6I;SV6xN(GYJ~aF3dn6|)CHhDnY=Z&tV#lvkq$$gkGq zT2<1NwxP9EwKd3Yym%$ZUGP9nhXnKvk3^CR88*hiJcY!vN~Gq%MJyY7Udiz(hJ&8r zRUxQ|WSa1&vf5YmZjxTMJ6jm(o3|uhcVY6Cs*d3<$+jpCTEiHMQWP&CRVl*f`IM$& z_{C}}7}skGa4j!5Wtb*L1#b`7T1^I#sQM6Wygzz){PNf1*Z-|={}m?Jb;3qk-vMN@ zbHiKe!P<^C{rubf{G~yR_UaIxv{@0i%)5P%krMAG?AV;!Dp+`EODLnbx| zxHUsjUkyiA#}w@$b=d0OxBE|qvzC=!4HFIy35N3xYmV1Fg76hJgDWetmX%ruRn(v&ULFAf z8sJ?!Ohl;rh-Uw7>?s{K1J~OgSmG{bkevu)pz}4|(zHZ1Ehm)&yKNMri@ljdsl5|CJ zF#)xgiddniDEXxLGAf^CH3t%)8Amsc!MI*K8s*b$4A7encI({>!=(bB=*9$4Us6ZV zp@1KdI?|0C5Cy9vST3j=v*qXAr$1$VdQ{V}x{lON6rX|yU_Pe10MYKA{>9qR#Pe!nPyv@G<2hFI{XYZgyljE zlWE#A%{~wd%ekD;1c2AILf1=J0gj8oWOVZwZk*#=uwS4MoCpz0+Ue4;Gk_~Y8+JO| zUiJYZp5+pTc16y&t-j-QdEPek9w@xq-}JKghO^-1?BEdLm@M(WEH=SFSS}W&p6i`> z;Tgv#kdJ$NjIQ;^lN*FuM->S&4kseTIcraToh-lk;hbzJX=^rJJVIN1^Amkk)oKl& zN5^Jx9k+Hw9oUa{?D56LZwLF_Pj87zMU#{~Og0P~!4~skQ&zf;U--c>G*Ht{1u3Lx zhv(xsK1&DhoM0v^9mk(G)0JZM;p{L;UU*$+!<~w1YC2`mytMqa6I~RssX!jRy&-S`Um@L@6@os=cs+^JagC5!S&G53K=84J#y&GKMn;sT)?F zmrv>7*78bGh@p-Fz(X(lKgVU#8#i_b@o!))&E~$7kNdup&1SD^wf23r9FOgB>W<6$tCPD$?pwwJ{}W}bvPlE zl-dnWv+9}QGEpuxqa|bl0q~qHO95&TC^3jnP>2AxuoTUhqH&-;fJ;q#ofdcNqo1+= zV2=@H@@(*U`}^hmPS-L~h+&V0UYXx6j?o<>ygq&VpMU=^iRT<2aeN}kO^`rBgQixJ z=n8^e*5_fk=J^P#Wm$k(42W`^1uHznMR$Y$7OxfO&{PkQL>4$rDhhuMV9j&XBEaLVX}4nDTphGR=|?DywQ!BY0iT9 zw3<9!zx;Cl-QSwodmOzVt#F7cBxf|T%$-wrCR?M0W81cE+fLrtcE`4Db&?(1wrzH7 z+qS-p^DEAE-PBEuRcqpz9Du1%+$_~AQBH5UhlP&z@d_P4rM!FfK-A!Z*K10PeWDf- z>ga~Yp8Cf~!9Tc)*aC%x&ynZULf_EhX(BN0(m3xTY}v3u7}?+{VkxcMJz8O}tB?~v zr(xP92Jz<+3QDk?*2U%^EO2vH2K$HSx!D>v@BXO7vO@?ZZZ07|;Xd)Ne~kXT*xQ0V zpT)V|0WZe>Vay><`3}@jnC=_TH#OJacxEq2oAXVy(&a=HY**#$L$HdrKUB4Z4>NgurF0BX1xkONRfX!7`sNt$4ki>OwdH8u`zjKqSl4pxxS%hng& zIJjV=?#&0Ec7EP=dV06EmR_Dg7WB)ssbvBSjyu+@wB@G|DvrTjMn)PahR#17a*j8c zwpV9>)>^+<6X38bsMh}6G2?2!~2 zQ69L21Gd_plafOEl%|;FEDz3^h4hc_cLivVk7Op=3fY6 zOn*?dj~s=K24yLeivFD^10g8=C5)sn#<@fI(`Jlzd8IBg+*+hltL2uBOd$}p{5``{ zpH@*5lu;_w;^+I^4W-=;N7dKt*GkDkn-)j1M}w!IjoY_f;2&^+17V)>MCOkmT9H$} z3zDpYFtX70$R=l#n}Jz$^5hOW;mT|R1h56(y9O7^>t21ul?ILa2FH`iwoXD z$AtaF=X?t$+eMt!fFm$pNC8>~%N`9*|GoMx2IDG>aeJ?QPL@wRx43S>KETA_5Ou>Q z`Vz_Aqp|Sv(mG0c5dF-NX}#6*Gg$Lu8<3Gj)GV3O&xBwJqYAOS)D%gC){uqxb@cXT zA%8J)unJ$JADy!f<3xUes$cz#9X?3!rgzw$=o76J=%DqGJBr(o2kRezgw2+?8~2OeGMuT}~6W&N{XJ zr0!Q&-Py^-JD6Zx?)sCp)CxqlmDL9kecX}RgJdtxT0ID_A$-T3{`jKi z3!T_l6_438mjnl9cGD)iqDP8ac|Pxmc}m))iEy!leq~?q*im&8(JjI!zHP(Py*Dhy zO8InHY`L?q<7lFkMbFCabry2Y_J>H{wul=+ntfKh@N!d z$kKHJp`!}7=@ZZ3cI_^#@?aZ7`5Iu-d0gP)HmK@dOm>eFz&y&qq>SPzTnj;U+6{rp zsyp%XfEtv^zR(Y!^?+wQ0X-V}}s92U7!V=2o_m&{u$E4+Wb z!HbdWUv;SQRKh?Q{~(F@@B48t|EH$WC%z&W0;KEz7vm%<6w{lVL1h zVvVhu8VVH^O!l#mc2mB!1mHIkb(YD=2Uk~dqxS;@o?yNQ!C`aqx<8scnMq$4ltQ*39sTi?s=N9UuX^d zqQN<3z>23wpP*b|HVe20D_(|5IN-9sj^qtkR-)yzuO1`EJO05-8+ZCOk3$KCK_RCe zVxkj?gpi1!XV9plGN>jS$aNUjtlc-R^Od7`GhHaVr7RNZ8l-cGCizSd)IyExqkg@G z8~>R%ggc>0T)kXiET+9Um8A>oMk8q5k6oo>B58X7+|9%JR_Cw2a_}oPV0G{8tn8V$ zowLIlLiiV2^{tw+d8Xs>cWmQ<_Huur4C4x)vi;XK?oQUdGc#+@{q}bA^G?`AP0bI0 zsG4cK#$C}dfg;9$OR9LOgINLZ(HDa4ikQxA|ArF?$Q9ozY*%fK`#j}|(En%CDGW}R z8{vejizZfji{}w~H7!Ptc{o*9xqTWZ_Uem=G{D9RcM5KAtR}x{i07`rsi7#15S+pL zt-I3MlIUr-*9O(V5G7>qv2vy_>m-S05-+D~(Fbc+*dM6iNj#JzAc#ob0 zTL3D9{DZb7kA%b)M~L?#8ISSbncI=XC_KkDZ-SV>eHZj(2b-CaJpL^C_HUZdbPE&8 zYq6mJbsbiyq8#2}ess(WXmBZ0a7*C3xs1v6A^J-3A3_+PO|H%%l+*H4qLq3V(!VCr zE-R!OA;)x7r7W_O3W3EK7__F|*_8{Pph96!G}H*0mgM-9e5k{29r+93l>?7I>CR^E zZP0@WgP^~+qQf;!6`Ivrb9nFM7fwPy13lmD8o@w%tB&T@a7{2vSEVnoo}(ly-i^4@ zc&cMcAwdZ&0X$C8bbD&6?WY@4YntpZnGypYJ_Rbve^w*euan$8K^t2vPK5 zO20O@;9@({n_uK9fbLymwvG}B^PBZN)A(^W>(ro(W0L{KJ6(DK2RIh2y}AsX1-6IT zpo=pbnzjkP()cw19Vvx$kdNhxyr~w)M0zCP$%Z90z*a)>m*+S5O4V~tB??4dIK_xn&=5AWVBH3c`Sd9Kzbp9#L z4eA=y6Kc)M>ME%ZLI_Db4j^!*X6alW84$CcaJJ(zarlFwj6C0DHuR?Su3rHA z*Zk}q^0&W)vvUDxjUj3fdj+SxyCF&`^Xkt7_SHsS1^@Ch^t9 z1CE$knv2v4xK?cRNV~%Fyj-f{+zYWeR=|~U^cMMN{I>*jXm=WwR&ZB$J_B{k`hYk% zUzD&kE_{@lpyd%4B++r-VP-%~_(+kWASgq;);j zTW-G^)rPKNK=?L*zdbq-ttiHvRCkPbLEm!Kr+6B+rzm8;F+%99gS(FkMj28K!IU@K z|KDpUcq!c(drwDak){t^JKAMX zhy(U}T}0G4(f9=-ew~L%W@XhWi-|1Go0y`imu40=d1N9Ayxw70S1+Mw)C(IhVOxU5 z4=Sv0OEew_@aM+tv1X_jWiLDpkM@7|J1B~I{wwlb(ef)Cdlgl#(CqDoyAyfqQpvPKXw?4J$drDW;tM{(I(-WZU*sE2in4w#%XXKL% zc(;+e93dg(2OLggf{D67eup>uyz@pWt_EuBOu2G^xj08FfMZ_{JhCxY$ao|cC8{f( z90(;Ssfr5lcuZFr7+me`JffTY-Tgjv^!bTw0TKUZ#F+2L0ldDEG<#vVC^;S5G*I#A z?c_wfSn&nETn?o^*i8y`u@%h9P5FdX08}n&%#J~%shEQFj5&DzpnwC*j$wIr-^HuHzf*J;=G>a%)HKjlib;;|l*vnJX^6d$ z*?&@Z_CgARAk8_-ZEDj}E%g6XV#_)pn1Va9XPi1>jE06KLRO2X+XTJhL7DQxZe##d zl`3)RaAk~py#J~|p&ly+{WW9MWC-NFx~Z>GJL?phB63KOuzxgX`2i9&(Nsa|R`8o2 zZ}t8_nKDrC{LO)>sxA^cAg6?u7UsO}c`kMgInzT@E6uy?dzJ!x`tvgIY1&Y|$*Iuy zfUjRMqV{@A=KSeNM{yg&uF9XeU-z4~q@9JYpr&RzRfA`P|Lr~QxuhDQt4T?d zEq!RuccgJ?ZaA)qKdXR{2?g*81#~GBP;+nkh61%ST_ZmRf{4$wAAS0t@J!p&S?DY| zIRsJFy7fN;jW`b>i>$hF;F!lJgNQL)AM_uUUaa`PB$;Gw8_qO@N7<_28yPGd}=txea4bytpF&^sA$u z*oxa=R`pLKs~1o2r)bF|t-=B{!!my#0Dl5p}me~5Su-y)`4now_jKx46l)4ht z(db^0ZJ5Nmo!?{;qcwu6k9eZ~H@AP*y9Y~5f_{4zA1X_y4^AXhTATa=-EUod9|@+; zlT*T^Ila6&JxbF{p~i0<)^S&}l#OXxnB2SdKsNQUgH~R!(>cpvbX-#7mRMh0Ym#5g zmwH772ZUFIe$WS~Kn~s8m8!y8q?7A_b6>z&lyVKHo2$vrQz{;&pmthCjBR?db#){T z`JaRDXT+=V$?wBvBl#8B_4kgbzmjkB)9?z{`=}1pL9bJT0skbaz(bQLRzA`zyi9@* zR5OjnoW_^5AGE2c@*z=7 zhHcIK2RC)lm663FGBK&QXSbf}gf!{GBEwyz~9!O%|($FkS+Rf7=P_7MJ z6?BjWhz}TVTe1_lI`B~EZDH=XlZY3oIRqUK5Ow}8)X3q%UCjhRN#W;W>dC4$ONgE# z0tjcs#^mWlo;986&M(0w>4)G_V%=WBt6YmRO!@! zh>7lGx_YN2%}W$U?jKb7{&lPQJOw&A*%J*j>kf+q$#ihqKv2;6(l|JS+}K&c#ZorP z*F1TpXpz$aC@F!-VzLiaUEJ_)r=XIM`E2)!^y)BXm{AxHUwf_fF9+BJ-{!4tUOh9w zc@Iot(>}>IH)+ORth+FI&YflF8$=j~aJzbZpFI!n-0zvV)QgPMrPu(@iS#tgs?Tws zXQB~*va-L6cpHt^=+WsH)~}y#gxYt$YVLRQix$HtNB#NR-rKQf?y}OkG$98G_6#-t zAS>l>Yr06<@A$lHWck{Ab+SVvE=aW8!c@St{i*oRyA9unuXPi6QfKv!232;T&lRD? z+yuKf-5N{<9Nui6|K5(165GqDlXUJ z4Zg$_S+W?;%RcDSa~qutu}z=mJZba!t9?Z`@9S@6Un!GxTEK(x98>kz*F)~$bFmqt zi!baS6!zB9LQ-ib!fM#49$QZft8X9YcgYO{RqL7ejwFhQ$qhz$T(tqux|)=Vo((=Q zdQw`lcF?M*B=LEk=b&yDlLG;m2~EzDOx_!WS%2iC>p@84=JWhX%V&zZ!#PrPbf8g* z);cdtDoeOqzfZ8rj>cD{mKvF*L&2Sh>+c=J+g=oQk&H6rd9m083hNLy0BKPq4zc_q zlvRO*6zc!CM(j~pg+DfJ?G{%9Dclb3uFT=13)dE<8^Qv&!i*@wK@^I#uoZk}kfkN~M!-rP z<0qD%{>s1YZYCSUz>!}93cH&B*VEHnp4B7=!+O`uMuXf&2D`k*i0e9s-nN^3Vm;x{ zNq$R2B@KNAN%ee}p#fqzPMe0C-Uf%6UYbLhSKN6=)!BT>n_&3&#ZAw5Mn(^TFL#IF zyW8UzV3aSHe^XQ=dbCjRYhKsQ@>;9+YGGw`uUC+PP&n}?0vbTncTJ@w`2G0UIjYe~ z?%l@YHJY=;XNtw=AEdp0o~E##$2uLaC*KJDC2?mgpR zx-pARFd}H`I(M`-H@Ie>eu|{PJT32S%8fdS!z#csmag+>W5>x^!YW#6VA_a{%lWJM zYMQx{JxdsEj~yK!Q&f&+6#IRTh5p&7qcu1iynbc^o)KxrY&gj*zfefC8n((E^n$8D*E{hZ?#LXEd1vPD@KJ*pM;K>oPNLAz`KAwUIFvLv+(!823vI)%uYA@EP>uuXP$2j)*jz8ruA^dzVA zv;#6YHAKCIKke^rkwt%PhyE#q?q91~dH2C7a@Lb16l!e{PG&1;D|&C@sdBrNn0R*n z8`?kx8qgJ~*~G;5Ynt2EylT5eUv`0OEeB~|#nfMv^6dy1Xr7oFXpo9DOT40`fukB4 zDd~!m@LI?`v8jq`86Wbq>S*X9Cp&!@3HdmCOjIB_xAFG#>LZUMWgZiF2H0mgJOJ`XdPL%(dKXB6hwR%cVP)17opxku zD+?E#(uiMi_b+lQUl@vZv-)sr@|y@J4DqMgWS2YqzP_wGO;9#5B(% z=0b`S&MPT3T;RGh6&81Q02r;F0M!G$aekH1ajjsP*QrbT^)8*h#)TKt&J@ zF^A6m?>Vk6RJf$Z`P$Q4E7FB@wCLCvgcOtr3+%NHn`|XrjnVsZm>I$uL?vi<>P5jw z3t{b0X`vxG$HOzGn1E7(VQ8}nBZh;LA9bRy?Q*iF;C+`I{%^Nml{NP~YjCXG&m|DX zQaA${l5hia&B7%EYjUl%#VqG&|c6-f;P8{24zx5Tp(&! zTlm^xbKPwi97`6pJpD>c^E`kH>kd4@+s`m=Kf~Bd@dj>$zREIV2RV$pXXjE_Y;625 zc&AUVyN6sGjcRv0ucoq3LL0%rC zWJv5@WrYM&Ue6`!I!;p}U~`2P%%m80Vg=x)c;#r|Ds18JP;6OF2Fa+1w;K~ETLjv# z+?Qg9r=OTQ=Bs$V<_j&sEu@VDfjg=dQ0T?4bIv_Fx^Kujt_l!&U4>Ni$en@j_%DyCxK zU>P}12N9tSks(&k>13Inah0fpqWn@yV$EYSf~Sk?*MJbpOig$$8bz!kjkRUm1v

t99#yg*DDx<0~zt|ZW_EgUffBVnj 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 z zG=l};wT@sQH2jsQKja2F-lA&R=r z#LVV7iMA`&*)`9}{8tgmS)2gz6o8@6jUF=Yn_ye%&O-L%=M;(=s2+1^d5{VUPnU5_ z?sJ?HYRl7OO;B97J-AXx<@tN8I!Dam#Z;;yKfgiV#+m3r#c4Xh`V0Wuy(X3-bl`CPv~(d9KTU|TO{y#Gr zho0&sjsp8OU-eu(%1VSJ1%kHy*{J*IfVOQf=rEvy*0sw)&{y6_tDFFggaA!$04RS+ z>a!>!px@YM+}B3*F3wg0;<-dY*1U_1uJvS{p|xQn_;N~0l#X*+x;~h~4~QFSY1_z} zv1g|@lmyV(5bMkQe))Q^2xnmqCmv}UGI+Mbm54#y2_sjUzNB`n^?65Ro9PiXQ+?{f zKCBZ4%kF}dXnKw?7V+cHhfm%6`UaEX$1&I;3hYwkNdC^@uu^2d%LTq1re(7*Otcm~ zx61=S-eIC-yh%HZ6dic-1hdP?9hI|jASW%kT-)(@h>J>3yL|mG?h2~T_y}Lp!R)u= zcq=I1pHCgMh;F4pOcO>Qku(%x#6qqG(VQ9(HvLmfP$-2cQp^aHlOhU3ePf-LEZWqQ zM8NxjxHSOq&=jz+^tn-~RRDt8pV{SCT@?QRj#M!SR1|}Rv9Ler9cL6ws3B^OP$|P_ z7khOv1da`K0@uDHVy9y)Ak!6l=+ipPm2M0M;^u_XVSu31^(iHNN$KG$ zJr*U8TSZc9`klD~HEl={-c^Kbm!3&V4$rjGGyS*sdRg)s9#WmWBZz2IUU`SXX42v4 zg6l}DW>@QD?CR=UUJo{5mGF}-{(xn$?`gxE1BP`>^1xdKc~oeDj(llRd>bk-+(cpH z+uXDk|1SFGp`O0u*ORvuK|-q{+F37}><>-0Twe+jfx?Yvrxnd8s^A<0<+`8CErvzfv0H8Fopqe^!Bm7Wow9fN{2vhPphKc%62juliqBCScYCQ93Cn&rUIA=O47>I`QR|_+8!tjKV`d z%6tq@!Oxea69qeqA`;yjKPMkf5tmj^zzAmtVOwkto_|MYx68wfPeq~1s+EUbQ3V|k zEr9pOZ9?;Q_>}q6r@})+8(KQDjC;*A!JF}X?Hv&tR2p&8mT-BUO!WIYq=`FQF-^pI z!`s2E%u~ZvqJcDji~PgOsYtzVSOKOaUC`8zFpEN&1GVZ7)M6w+ZCfeUiS`B&c};{` zDxx`yq@{0b8XuL$eId>$mI!LWw(^|t zQ+X0-Q5G4&tCRI3jlq3=WmKHavMvrm2X`1OILyEd?hJ#wOM+`~m*DOMmtccK2u=tQ z5iyBHyGtPl&tRIA#MJ)Ep@O@vb3iuq; zUf~stx)G>BkTxYAnSF}iNcFiXzefW4tN8SUMr@E3>1C~W>BU9j_n@Gr1MkXc`I2N+ zr&jPf%kLL>{=+fV>>$LHt>W`#LsULnE6E)%Wps_MR2R8FT#WAMa83y(#bG*4!Q3Hy zOeAj1mCtkXfG^LnBFAOIw2GMZ-UnOcgc6d6=C#KHU&mJd#KjKdIWI+v$zUxruMssj zpu=Q28c+7*pQC}EAn*7%Xl3TCIHZctw{XkrWiO9_Dgt_ZkaR77xH4o{J#c@TY&y!1N0HCYH7v)lz3jRks%nX{7{01k^I(1r>o3nc zr_dKGTsOk1Jw-*)=hx8*1Xu@q`^yrl&E~Cv(>1`2vg{bf^7y&nM9kbR!umm2*(feO zA8&EoD{w!6I^lS_>AObrtQ1i~p=6*`AID&#Jf1+ZOP)n4 z9|_pfvqq$=DNhhaba4x1b+{~FtlG@)$rfSHS7qj;oQUZ=-i@Y-Mj)4Mdvt zh^CGi*s{LFGrT#0GIR8cT9DB8J@~~02Rn}Cc*u+fG$H9kd@Tu##rI5^P?mM7%2E*d zT9g0*yrti*K8l&CQd7c7(T|n$j$~875QKZ z6=<+j+NR^QIUxyU|2_j-bwn6n1`XO_Y?KH~Z~<9Cb3bdUSbGP0qy|EpKqBW#+P$gY(boN{*4ZSx=xE7RI^LVP zhUSOrSR!OTFZT`J-|HOWMW(Ds4xs@U7DHWw`j#1*Yni41{Y6F`R52e;TeoSC9Rx}W zentUm)qm6M#4fD|`{kGb`%uD+*6M<#cm!d-lLJ8>3Pgg;0u_c0cQb{-%yC_rM2V#G%Hu!N9+`far^$V*R&^18O8UW^}RcnmgPL!RoHB{GZoXdiI%a-PI?q zD_O3EGZZY_Cn;UzHi!u(LOCtmH25*3?A z@MFC!axU}fVXM_6M1N`hIT$}c4)u*_H=VX1Yz-%}aAuUWp3;$Ag~+;1gR_T-O6=Vp z#i(>LYKe*v3RzC?LfGHL{us?TcRVBNjIoY=)MOtkx(FGZOAp=Iw(6mnmb61JzU}gN z;r-0qKDc)D3?)^lP`4nl0X_^`{rF8tD?f=pH!{c;8Q6|&Ap4@9!A<^y)Mji*@y#|+ zi+-MDjP~wrxEY6~{C*dVlM!IS2{5KY>^^h8=DeHOQO0?H1SquF0d67ST(Ik=0K)z0~BiOvQh8 zwVyX0sw;mhB;pTzA6@O%*;eN;mZok(pjMOwYd&5mg#mm+)_DQ(bR#?8NAz*Y4sA*O zf28h8jsPmfV{=#ddlO$g%rC!auK-qSpF`O$ zFGIFp(kgXgAvnUN9!!ENQac>|w;el;nB-6;BK6@NB8;QgtA?U8iZA_KKG)>^fd?&~ zSfRafB@Q6<{U+UmTb;hmsn4rfeUq^`>1_{C%Nu*uwXu1QV zirUu-NGwWr6{uG$8&sLKbV0JWp+GXmw-~W2C0Guh(GX$@+yLzHfYgdt?r}-b%kNNk zNsMaw_-s+e&O$BbB&VbiT6XB12=3b zT;0NK_n9s8s)Jg84P@Go4yFvtpQEQ&;#v2}+tktjreezEjA3J)0J(DluF4F4y+S2q zMn*`#4K*l^8o4zn9Y!7?)O5ASnK(>x+sf4zA>6`1dptC$X%JFRaZ>7iQtF`6V59Pe zhq3f^K{tR!8x}%RKu46g^PVljTIpPR+`P|smr#y!lqEdG-;eD-2UhF$4K6l}W_nY6oY5>b!U6Bb54IAQlI-Z_Yf1Fc2`_k8h(hT~)}Bed z^C~w09&3@|GLcFlh&L4#lg1P$u8@#0NM>c1r(<@F*xF4h=o0kkz~v1^Z84ji zi3VFJ#prX#Qb=dGTKq`6N%;r?TW%>XD0k}#-o1sq9mARrl9=;nf+YO`NJC?*S7E{JLp5#Z z6dKU5pV-%FW?clAG#c5Vw+)k5JNu`o;WEg}4j(wAT)ly8wA#3dDV~Jg?b~>zP-l&x zA)BIM(QDJglbsDxA5X%BGymFF$NboVhsrTy)% z2($U(gW9@f%jwvfn$s^fMwO$PH|X562eV`Xbghf#ht_o``V1`Wf5r z{S1`SPst+XMq%l@-fK+`trr-~+;^!?M@}pBN~5hzrG%mfc~rkCzxbcv5Xz0X5TvUTPoi3!v*rs8VTz9ybjO2zjN7f_}>-fty$RH3E>XVedn~JWtud~ z7o6_8wj?eJs0mY^ATB^+M+hN@Zh02|=ul=vsHZ!6u!hmZP}njzpqGSxySo==k7lVm zH)%H~a2&QHueRgL+o8C=40b1j5~Rt78QAE;IIOF#F^J^Asds9h$x-tE zE@<38V9dG}Ifc|a*;Fy;eaihJrU|u^-uo`4ajn?_!QOv=_xpVSlSHXZ`)B>w_>K#6 zMkzkE*G4%rJ>t4c{b!RFm0*Xc{W6=1Y5oQAuAf-}L^*LNJ!h>$KdLW+c10KRLy9?& zi`Geyeb6jmKLa!>sVU+MQ3%&a(N=FfG{#z;bn&>l-0NA{cnWU&1sP{}kg_R`YBpaX z8|alg{jBkbw`RzMmt73M8w!73x+A7w6$N2S?-njYD?<$J-g0{(Q;Wnb>o;yB5gX7Z;RNx zo3-T{wpg*@KqX|rn>J`e;O5d2V=dcon00d?G=CSS>7`@%n7U~al`COD=ifC9>u`q5 ztkO#l{cU*p{F66@F3Ej{s0yQR)mH{4OajW#6f+9L5HSTtLwv1=f)=$&=+11|vxc>; z#lRY2uV2n$WHGd4i`%>X#rAy3O&H6h$avfC@fg(g=q&7g1fgqXVg&bSs{C&+r|v#g zy-A0fFtg^6{8q-g>wV`FJ}gl>dR4!t^~dIQidhd;vXLss)*_u?KY8)5se9qIiE7%4 zwr%zm@+tF`T!F~kv1e`)V+_H7kW38bb6NDftchIBh}-%+=rkpX6o+|8r)&rOew7Zh zDKu?z)?K~FeB3ck_T?Nq79MeV;S#5BG|?zFHolel^k=xIsc1i$8`Utz-@M*zCJA48i%k1*gu9vTNJoe-yhTifMP=C8)ChC@%{x4m9>Nx+ za3X@9eBSDoI>N#qr{E8Dny!>%HciLF{wfyZIAfc|Iy+o17>!qgZAW~+j5174gsEeE zo~x_qahN)#QkcgbQ*BXdr>=P4n?%kgU8P2?iDL=3|76)M_(p;vEt!IONn%;bs8gT_ zqd~RO5C4G4=JAYv344y62ob$c>#Dl7uO&Qza>26=&svt9F4!yIjx}Fn+Yixt!hEKxkyN@$~OMzmhnHu+`Sw`bvU-y&VBrRs48AT&p6Dt zD=tgP`fsmL!ZN#_lN-PPP=j1p(u`rC{qCsYhxk${rmQ}0y1emH-XB&&2_aKECY_wN zhgv?cJDv)3uWuYz!*|)*v&3${2LlUTwhKcDfHbp>FDylHbuILL(l;~YU;x!7UUMGe z^wI77=P@1V7_5+GO&W}z1m=A?`fPN!LGtoVb}Y^=YzKPxZZOu zS>Z&AcW`rE4djo;(U8*0G-E#;aeB5&OFr}*f@+QT$%)lL*dz#lO|74C4sX43 z70F>OHEV23=>)FfoDvGTs=SSmf(;ZkcfoG}(k~)@b_3Gm4V6UL1)Mo1F%ED%lE=-e zKO{d{Dpn7AQ`76Rr!=sBiKtCmU?uu)(R~f({x;(O!xUcE_>2NV_@NVXR#r$BmBTW% zh`ZeXGYAU^Bzq9mw0B^tU!c`Y$~OuVZ=04X0Ij|q+_;oduw%)_>0REg5W%D%p!2E@ zuzA13hw*(3wtDq}Ww$&`nVyFFo;WgC#B181Z(2Y9*em%6lThu-EY~k;PxKciZf=E5 zTwO9w`ry^7R#{qAWA8L2lAsk}>T3+8={BW1klC5==0iA}8WXkHqOM>^zAHS-P<5w& z?WTY~Pk1lNde!2NXig*iBrm7@Bb~=!4s~mGg+;r0dzrN2$3?uW;(?H#yxH_qoUG+| zv2r#}ptWX9_aFUUVr--7xo>!E*EVSLOh`u3F~4KLg8c@pj>2Wgb{iOlvo`ZY-3Lg~ zlu~Kat7N@*o?#h*Gp?jsR!}kG>8u-51eqNHZZ|MdKJ>v001cGigrxdja znRI_k59vSs;rQK=dzB@FMmh{0%}&N$obX04`(NlJLWUxd5=u`}t;nJSSH}%2@g`Im zMJDm--P71Yg86ThvP7~qjux@Z#Hbk;3Mg&iYm|-c3_NhAP^Tgcxm+X(e} zx0x3NkN}nhXh|aR2G0|=;iDNU`p#}8?rr%4f5Ie^rWP)gM2ELTc3;{vbdaS@6a4-5 zdmMoO_#|P;tUsiu2`?s7_|!4MI99l9V~BA=ru0upxRT29n*yU>Y(WfPWpK|Rz%4(4 zqS`*uM(yL~r4x4^GB#LZgB3M{UMsfwfd4dBwA#u+!x>6_w<>W)T<9a|%oUi8Q{Qf55` zHY~#B0mHLe;l6C4omYKD+hccB69s>0!oS9Tu*iJXz#jLkSmJ^5H?v3?EHPCdIili6 zSkX159PTJ)NV5s-{Pjk+?=h{pE|TbGllnp$OG>;pp2n&gqAx6X&e~IrNg2zINBN6J z1rlH*J5om54?^hcnI$!D1RT2yo36W1u3f>K&7XGx}qQDLf1V{n_|Q6fPsxgIY;J>w7fn*knm6 zQJ)!FBPGp!V0B%M>RcfTT18tq8%Q{eI6FvHTcM`4I{8!F5Jp%|LGoXxm4eJa^Mdpe zUjzRhuHP00->i1kB`+VB)CCRt>RxaB?)wmYbG3S}{@3^Q`TeEK(wE!KKFNo>W6TCq z$sp7d(FcORR~P=d8m(1sgElY|>cjAlA!kXpo^9q#k4@g}1*g|ulw1~HklEwj6txav z?wc2`WPCW7;Zrz(4koeEGd2y{B5FAIXy|zyS{fp$1dtSrNK=Ngko!}4UTMXJ+erkn z5KNL`HKE1q0s`lDDjAY-UPS!pL-=T((>SL&Sdu=6NEG6h7Rp-L(hn_O1FZ=_KP5kS zVeT*Ms8O)+p*DpbR$NW0oIMx-`AihX-@QJ2;r}U6?Ke}voXPu^ zF-f>2$?{pq&M1;Ok)^@KND~R`vB}(bQU+pLG<@lZOd!BSGFFPUPA?Qux89Ys=l81& z^=#X`@tJvT?{;H&;X3=TJq(RYL0zmBGVPG6&G$(y-wZYI?be&=k4$PglD!hnI5d4; z2F+bU9J=Mn>ZZ{irhi=B|8=8aV06@N=6x}AovT$)yQXnvGgMq@jzEbN-VbeL%|w_> z5D{K3y&uadlSl6Ti^vxE}8io z%4US;1(Ij-NO$w*8+cZ^D0%`Yqv=~5ld|ayC3pH8&J+EL5}Y-cig<%94&%sq&A%4H z!X7;6%ZvkCiu(lw(=N(0dw1~9xk_cLKBuBazl0aX`a~fy?h=}E)WVNui_V1@Z^`A+ zh~FA&*T04GEgaYGUg?#75>xJl@%==Uri6ET$H)LJDZT5}Pk$9pC@Y;E+-wLRX(St1 zs9LheGc)=jCnhI4ZF7dQLn~oTlx7i;*#3Lo3=tWvy3@ysfv({T6 zQF@DM?NH6yF|b*iA_zy%*9|*U)vzIfXTtiA+wG_tdQQ=`R!dAb?zRA9 z0k5Ao{7%}n*>u}w09sz1TE$r0&SPm?KaG0m49cc^CMdiP4ny?ISSVJu+pZJ8lq-sAu>UdBjM$X35y!PAhYowpMT{EUC zSz`DhPQB@s@Q1{6!u|*Qdk8rC^{3f*MA(v420LEtOb@ZpZ*HEV%ecUN)64uz3nOHn z^0CG9Q7@**x1fBL!!^-aJPiDbcSBNAYjW+3VSbFXH+po}dtqz(cJrqTv?sHKk{H1> zAK|=Wf&@d#&G?~sl-anHUtj@)%O^A0+H_3znuT8w55v|?p;p)BuUmJn@F`@%mek`r ze+U4>(7V2U=M^KXj+UMdT30bmu$n+mlY~~8IL3!~%pYjb@S7qEHl#mon7+%JPP9=a zmDA3_f^VvdZAjg{Yx(VcibTX)M4x!`sbT><|Mrxt_hmw9(E(`e0iWrpT-iBRrG%7F zru@sISvbPCdj#a4&bTkQW*Mz{rp1G7V3n#@5pY#}zzV4A)aw-~oH)(Z7^6R;V0_*Y zPuQ=Wks^`d^vj2+yzJH_u(r8g9u1H->6<5LL$&Iz7FPI;bnkmFzUex>VELJ>uHQ$5 z#pIk^KJ5W3-$Q=HN|kcpD1%{^rjX|%(*tINW)t}L?V4Bf>QV$y0M{u zhfD?o9zL{P2jAod`Fp+(aRa9Wf7|ImHWQ(bJW;GlEXqkByFyKM|Iw32sZ_h^z)|+a z5#-NrQ=F7F8f3R*~*BjYZ*pk7)MvxJ9$;cI2bf=KR&nsTDxtmjq;k z4`E?)Exqe%f^zdVFNOs|D)x=*Qm)90=sW1Xz!T;2YC&Q?ejqV+fGJVRgj(7q!H4`; zKhyH%?Mt29rYv#>={q$ti7F3lgFqn~L+%eq-Sz1FaYYe}GnrVfq4WT@m&rW$I3=gW zk@s@f;wONhoir{cb^Ep>q?^FsB}Ol0-3~8JHtM`t--l{ypVdguVp7BspxNmA*E0EX z5qY5_&0x^j6ck2v(lwW_qLp35Q}GfQH5CNv#hUMIlICG$ur%+g^D>TQ%vRsWBgGR- zFW(g~D^Nh?W?glRNkCOaPmiSQg`yA{C^E~t_B+_QFJ!WE++>FB-ndv*0D2D7{IR*P9(dyl+mn%(- z9nRck?7A!Bz@4}XshY$uG;&S;zJ+7)iErN64~a{ftdbRD#Wn8+`@`zslIeOQLWi7J zgAFh6M?MvnM@!oj&tM>v@M!EFKWIDSKQSU!L z9z>)`--9-9QaU6M+iUhKy>a@($xRiy0WTXFm$#7q)0D5PV^_O;PjBpvD;@X_rAR~& ztLywKzkCzu9N;sMFv*9M5xa~WTCiid9jy?B?ra*`;-nUnKN-)z`sg%uz&8)AnTpXtK=z^iOtCk+RX(r^S`k zYD~bK;<3^gUw0gQA^i-aAJIG@ReRZSELu0aGi~2gyTrx)^ULei2cs%}|_Au1otTa zSjCr^sY#5GFyO5RQHf825jGYtKCa&}kpNIzB?v5~d?H>OsGbKUOc?}a+0EYIa~w5d zZ65(AzTu*cq-%||Q5@XTo zL?MhRZ?Fnd76{=}kt~}@=P497}lSk!q9z9mgMp&6IE-5=y@P}mCj(U{d~3+Sq2=O zU?Tb#EZZm~+k}6O%0r*AePHR_qX7H2*9JO7Vig<*5QX1V8T}YwbI42cM`M*2%u|~8 zGDRC$S}Q?6Bp-pJj9JpWR*}I>kW{_+VFAD!i$o zcG&$J*_KQ7Xr+5QH6_1)3*E?hazg$I-e5I0(_v+rG7)5Qu^Fo#=i=*}nWxXtP^*92 zHx`yjt7D9_cGte3@^w+9=Hp_8@S1euaQ3it+aJyaVI8$bDi6~#y8JrDuC7I`$%#l6 ztNWhJV~_h;v^ZjN_%{+E!?>>7qMc4ik`;xt{lyH0p;pf`H;{An#(4e$1$tR)=UHpZ zG)K}$lZ8T;gA?e_gq)s->RCgjWJ+;D5-PB%PvF%|oM5?qezk>>Q8|C|iSN+^R^bOhpg;4l8o)nN*Ds1NtPzqY-2v$%e9F-x0j#gl#$L%L<=|CyCN z@l4Y}S{eh{l8U^x(|%c-9i8+l+_ac;h08&Xf5K<=FBQu@Wu)cRg(0s+dSQ&FY4?$$ z6LZ+s9iz7*n$yKm+JvdC$7iM3$`XNQpQsif0!#pZYY~^IA>x=n@!IG6A{i5`9POQt zAy?;-aE)z-(i=JXl-Hjo-V7WJOGwkX$uqvuU0>BMH1FOqz=|6SkES_5<)TIF)ol?-0DgwWCd7&7)U#$K5XlBXhY zs`m1Y|Jb_hg&iB?FT!{|e9^;mmxY9iRMeHn`k%cW1^&CWqcQYxp!MRP2`&~6co4q- z+wxJ?-P+vK-o+Va?rF^i6X64a1weve2$&Db59a3t39*7etn9r1n&sloCjF>y4ODb= z^z!g@H}`aL2g=F7pwojMGbcsD=$myf1A{R!GVtU z7VhTmen2pk2Lxj0)v@<q@?=^%4AWwB4HHH6k4L+eKm_ZN_KlrJ= zM=U~5Ie>uqK~J$j9uwjze-Nl3-%~7t5TU0s@FRT?q5tg%7$P7DdcqZ0h)?LLZ+wr* z^^_|<0bya`Cz$!ce0=;**5HQ-@e4kQ5x=02;D5-3_@GaDfq=k*{7<-ofFEn)i9HDa zqiaw6fIx)6Ph$xY015Fu;S?eu1bxc$W56N8Pk4rcp%CyBo}vH5;|V^fAXxAzJ^>II zEchfh1o%Oqr}^?vJ`4Om{5@8;y`!}|o|qV~w!OdgW4ZBaxwv=&AD0Ab@hUpoxB&n4 z7kM1RjKyHm{KB#zK9Dq@%s-@qWCVpD!v>X=0n35l!hAv!Vo*UCOh``p(GdX<1TF}b s77~^g5R{dHfn=qHq-EtKUOZN~ho`x_=fCRv5zb?B;jysDsl)O93vx`URsaA1 From e6454d72087c9f652176f2e15250d6923f780282 Mon Sep 17 00:00:00 2001 From: LEO Yoon-Tsaw Date: Thu, 16 Feb 2023 09:10:00 -0500 Subject: [PATCH 023/164] Fix a bug, that utf8len returns length in UTF32, instead of UTF16, which is used in NSString. (cherry picked from commit a533b4174cfa0b95b7622fd4dd2b4e29dff87df8) --- utf8.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/utf8.cpp b/utf8.cpp index 24c6590b1..ebdd5bfd0 100644 --- a/utf8.cpp +++ b/utf8.cpp @@ -1,7 +1,10 @@ #include "utf8.h" #include "utf8/unchecked.h" +#include unsigned long utf8len(const char *text, unsigned octet_len) { - return utf8::unchecked::distance(text, text + octet_len); + std::vector utf16result; + utf8::unchecked::utf8to16(text, text + octet_len, std::back_inserter(utf16result)); + return utf16result.size(); } From 20592bdef736092b041a27fcc84757f883d83e0f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 19 Feb 2023 21:37:02 +0100 Subject: [PATCH 024/164] Icons --- .../RimeIcon.appiconset/rime-128.png | Bin 4630 -> 3943 bytes .../RimeIcon.appiconset/rime-16.png | Bin 1807 -> 510 bytes .../RimeIcon.appiconset/rime-256.png | Bin 8102 -> 7751 bytes .../RimeIcon.appiconset/rime-32.png | Bin 2287 -> 860 bytes .../RimeIcon.appiconset/rime-512.png | Bin 16313 -> 17162 bytes .../RimeIcon.appiconset/rime-64.png | Bin 3077 -> 2018 bytes rime.pdf | Bin 323096 -> 324081 bytes 7 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128.png b/Assets.xcassets/RimeIcon.appiconset/rime-128.png index 02a9ca9fa607b66293e7e7da0a56878c09349fc6..924c79a3a25a661cb31f159b20c7ca8b53988dec 100644 GIT binary patch literal 3943 zcmZ`+Wmr^Qv_3PSq@c|3(V(=%P(vxDba#l9h@jFT3_b9HfCEy}DM*)qNC}J%LpahR z4Z_eNAT4!|_qjjrpYO*$``Ksj6??7quC;cIfu8y`N@hv`0Iq3js2G7W`tKmW3Vv}4 zj|kvI;i2)s8vv+ae+LA}%w_}tS|&}EdnTytt(=D*@X@?(Meh!73C+bkP0r^KN?xIG z1p_Lz2fn?Rj~cZIUl_PkDVKXKDcz4F7~oN{0Ay`FZb5$TQHJOR#>Tj#xlsMk&<+Y&!3@v*dr9wN%Pp8!AX4@hj26b@ZGnwQhMKm+ z%r$?ZTP`Nf+EugCEXXd2oS`8@z`k}omL?fDf0Hhz=ZJxy1>I2|lWz(*gZpfc_N@@+ zH_>b0|MIPZ23V8MW4Wx~btbN)Rgy}DT`0MWW8$>mTrl;uYhsd;s^S^k89L&!vYNcf z^W7Ywi(PRnmdQ0JW@cuKR$pO68pcjEdSC^&vUh`=G+Kr8>^DJ#Rn|+z`8UYi6vzy` z98kQJqm|z`7&>KRV+-2rlm1KotK%}H{m*(u!I6Hh+=Ewu$&_jZV8u+lfx!dOYK7R# z#hYI^k4Ru^%XzyD(lZ~o*J0Pn;S2~4{Y z54)KKfHXqn)RtK#BwLSf!o{rC$;uTDLvw33&A3@1`U+h}83llnk(iM+Axi+E@0UgA0N+>$$`5fDlT5|_Xu(C2pYp6B4ERiQ&6OBdMb(F zPZcJdj{L*2{eOfZm*;-y0$kf@g*i(G8}9buY3Cry!(;G&N%vL;k~O&DY|2JuaOWSD zw?~D`?LpNmZQ3k{*6=HPi5lE8e!sHc`ioluEB!gdvy;7u`)|VoxwyE59@Is=*g(4a zRH1%UJ?yS~LV({5nD!Z;2 zZ0~An26jdbjLmEU0t4x5?y}M(^>RP_=u+0gbgyx$FID)V25++J81z+&wyc;cpdQc8 zlc>QjMS{cOgls;gS63roUSyL=+i zLnaz;?=sVpV%_SS@kTiqwN{k2y1B{y4~CvmF^zB|M&%e3q%}cnL03$nl*E1bYX$=b zZ#tX~u~1l8xH;7rZ*Jp{e`l52Rc+mhI^AkyVq@dU(PJRTE_u!S-0=tVf$m-;a`bXN zSzlrPdzmp(TAD#9{!WhG!mbR+;nTs7he7*8>ZDLevR64x$K1fh`RPQXJICY4k2{IG z;l+rozTxTVZ)+nZC&$G2zFvO%ogeR=a{aINh$Q!iP=eu1BoeJ!edAoGJ`RnIb%SD_ z-AAnGJ2~+M2M4>%wdXjGRo{s3rh49Fg;2M)W&@@^x=L_mi5FAl*VM!_*ajsU@JEbo z2s6@0>lUUVtQT?vf4yyO%_4Tk?`_r(n8j`@1qaJdHM*}H?-E(%Pzg(fH1D-xp5Zm0 z&&eX1bnNj_2K=1_k&eGQ_Vw=T3Vb2ioAH}_(w8YIcfLQOQfTPaJRN&DJn!+-tGOxY zL>x@2hljqCQ&SUdfztBw^85SyJ2+a!Z_bP_Dn9zQyY5>1&EWqWY)pK31_N{IZvjUn zGf!5Z0KzwhsY8SNzMr4;_NTr6Pwf@iCK15RIXDuD+*vJ*r^DdkdF*1rK}%ohS!u|G zg@t`yycilC#Sac%KR-V|JKGz`mJdv&VnB$>$Y>y$pZNHg*r0DOCvvBU%n^4DKzMt5 zOZd(D^MU0-x~i(G(z4-IilF)Mc)DUDPlk@y!TRXg?gmX%!AQmUAw3uyU@`DtFDua! z7PlS@Fj!F8x)HBn0CuCMqo2k769fx-##4s1-0vIc>Q6Q`teqkMf&~G|Y&sp9 z(ChH@`SYa4r%vKb{)7O56r(nGL)ObW)UG{nxH-XfSuJkJ1_j1JlW%uiD!973z8iVw z&xv2HGPX?MjsZi**cb-7IxGz0yE%SOUthnuqa}d!ChB0+e4_R-SzKJ4Mv@AWIb~{6 zOL?)Ptc;>$q!+Y1N6-!=@aUK1bAtH+1pwCTooVlZ+7}yUsUo&Ci{~rdU0utgSJPla zWE4?2(tqy(A?F9?3g`RA3THdYg*kE+#sCyV{&HWI|7LAB>UciN33XjxN2e!BH`LIyEcZ0Ic|7riU7v)=asxrJ5$VcFwq#<+UnoDj@>>=?sH_~;1Cf&Pfl8bkc?3Y2fePQrq<-U z#cW<Gl zhktbO`}muM&08O*mOsr64%!*$36Kydu#UX{-N9Pu0gVB-Q--3D8M>x=S1|N)HDiK# z9U=-Mw(Xx*lRSBo-!?R)&UJlN%AxzAS0_B|{8h%tk#F8G24-m`V)I7p#xLI|vo5;p5{=S8mq{(SFyVd-}# z=SNW{!<^Vm;^@3b4_`rGo6Y+AIyoscfIY5c5@C5ol;*p0te^xE`9}ZSPoiO5e!7R# zks>q9MMDyN-5ALwto!SUggLXw3$#Y=(6Cena<0ZhRic&hO#%9MTwb8-I)drg2`r(pc1iJ>WKM` zjg6)IUmfe~$ZA&mFpW>6H?+ozqb80VSsedJJ|Zv5ZNHv2?#R0g2V14s{%q6)(MR8Y z_Ymf(8)O#nBz9asp9E}TU$A4ZO)KF}*Gooz{OC&J(Pm^}NscRWun=S7vTBLEQG)4Q zO%tYgM1G6h@Y`%WfB1F0&k)$pfgLgtkx6$O&a<*iD5CXyy0cc&V0Q+A~;bo~U5M#@YRtUDy44^=w*I z5I_&Q(cp+Jc4#MKhhLV0ZxX0Q_)7|MmKh6ZPswAZ8B(PXGmjFQweeCXzLD6P7`m5X z^|G-i%C`g~aaw8y&f4;s=(RKYV}?#_r3S z8Aw59o@k8m+E%l*6qi2cu#>tg){#?L{N7t3vU)Q02^_!*{M?}A{}jpE-tyq}UcoS! z>ax!w5+@>?6Tb9jEfrJNt3w(W=FoaS718Se=VT9wl8`md8BTUd zWJ;E@lr>pOB^5?WMGD`eQ(fay0N{O3cJ*L*U>uNmYKS(DK=mhT zM~2WqGys@dMAC5hAR+_ePb85kX3#e`+My6K!3^rIkAY)oR>VLuIy#K#67A@Uj}F2c z6QCC65YtE`C?JH$z(FEIf+=)lq#5+9TqHQ&ehq^{zCsv5X3&G%3LzdCXNVOwj0iE- zhHK&Ba5%)!Set;;!x<9wi2<4r1RQ~Y>FB@^2rV65q&^%8H-voqK|zIKgaG7Gl+8DF z;LHpf$Y9WrFjz!Hgm#3kHZ_a{(=j$ShQSfrGC+hDJ&MA>MQTy#d%sIS5$X6aGL1o| zQXtzBasE^$!wd=<`==Knv>#$A^ly;@V+M=F(O^2-@NKWY0txsZI2to7_^WaP9!3l% zh7c(XI*8Tzfu#jf8B}^8^>0M~`1~&l!02KyKXm+CTS7v9sGu{f!$CK`1?1nN>8?>U zBJ3!UPGyGSiPqtunR~arp&_lph&To{%#}(F{+=u6?fYdQS>gj91b&zoQpHK{yKn{rd3sm0~u7}ivBN6a_fr7jt z;26076->Y*1E^skIM8Ks2#!RA(I_M+7^oPuPWP)77!1;uLTBJ8c%m)J z3<`R!O(qkNdPE!%0Vg7~^b7;^z?)8hmJ!YbNhWg z;s3ebDU1x37cTfema|=S+oV9E$#gJXQQvC9g&6v66ikME;FI~7-&9hyB7cS1ol@azkd6ty8R9Z zo5A+*MNDL4`AidE{ih!PAPpw=xBd$*R?FYIroxDm@E*^E0TJDWu=ia7|d0E-#S;TdB~GEifdZ-7be}U26j!d<)8ieH#(qc8HB;dA6WGk1eH?>nS%r^LO9^Gx8b7k>e-krMP? zz%`f->+B-%g!}Ej=`bJ5DJ%Xmv?sQm`xM&BNV=;z&*O0giiKfAXPjBdHR$9gJOYiU z2!>pzoCY65nN4N>`6=JstPZgIm-J*u5!6P z`8vB0C2Gw_t0*eyyc1fHl!=xs0&2 zTu}g%osn>+E@@!E(P!)4Jv&?VT=BU|R`hsM99pu*k==D@M?$X+3TT(CsXg0qsFWYd z-SZlWxSxA`guPtuTy%)dAIm2q?d;;x@uD|Ji2qkcM8xp)G-kgPR9rAA*tpkL%DT9@ zSvI5FDk{qO%9SgTZO82F?Tt^a>xqepl^j&iR@lYmAYF6th}2lM@BPvYbNSU=&g8X~ zDf<0n-a_5h_e1ukZ|_KkZbU4O-zv6BMEUY}aEp_>5-Db zK<_Qor);2uxR>LX)bW)4n$h*a9eWja*#pVBrP=D8?K}$>R#&r6$(w1MUCo(P* ztx^mOF}O|C%Y;BKpXRQ)6zgIB;tFu?NR!<0SX7ozO35rq@@$`GVW0A4b%_m|T&e|H z%2H2$lrwwREvCF?lGDLZI2DP^37LBbxXNk- z&*`d2SQ;s+DemaC&!|dDNJx;Cl}#!X&%?}%dX&?Q6k9NsV1A^8ZS6DkxZba>4o^-h zSnN@gh&?7IF3JxkacF2S1}Jt$uO_NqQty+IU~&rx2&~le$9h;;QjLtrLuIX3`KBm_ z@$vDY8@f7jYFNO^%BsccR!T~W_miCJUNOTdOD2=)y@gh}Ju_3(%DcxCXlrW|6%*_H zP+e6;m}nqNxTxgR)givbobukH896vPW!#anm1gJal3pz=q%Qa#JLVV_b^VUiUE;gh zyK6t+Y~JMI;b~|}Ke0M|dJh)BU@#!nx<5qRm|ol1Y+V$c2sZc<)4p`|H%kaR!{VM9 za~~Gqa5zGh&F3}Tk9y~4y>w)T-k7fPZ9le}YYDOR^2*ES^*P7_T(K`I3S)*&mNzwB zxSO@F*Y+%`^%!hru7CLC+{o`PL)W_OeNTRx&2nOFm?`a4Fr;K>%SW{?-xg^ZwO}VI z$Lv?vZ-<7Nni7+n8X8`?t*}^ijMqmUFPr8bcYT)Td@we=%VS>D zU4q^by|1xT(F=c=ei(6SNwlr|$F<4+y&0yrzkFI@7A|l&FQy}&oX=)e-c162xdnOC zx{|)68-Al8Q7JkJG}CBbMPh;DKClCNM|e2Y`& zSKCYWOG!!9kcZWjPAt~uvxLAj?=)Y7RJXnpwzjrz8Qr>0f4MeaS&eAgV}R#2iuq#n zrfv1u?enm_{;s+Hyx*&ARf3Z$`E2bw}AyUwZFpPSej`Sdp5N6uvT()5i-?WfuwJieYR&!_2GcY3tay*~I6 zq8yWVVPtN=Dse|`=yMw+5_$TW;?C02*fYX_zodmp_#!&`BTOQhwlrlNv-yeiacR02 z%|s z=-z#N0PUeqL@jf{jm2UeolsETk@$Lk{@LQA`g|larSx#d(Ab#GeyKy)(bKw_tJ4w6 zDJ3&+BbPsKOojDo^hAXQq+ zAEf2T*qCFF92XWVnWB+PLOnMRea7q66#SGO$0OOaVo2fxYL&L~jp>meACWv8?bi6; zSCthP$7y*rJs26$E3PP_{{+ZWuI264a9aj_eL1pr%JfjNIH=3i_vg3q+5MNJ_5f)q zDZ-bd=HxDD``8hQ#DRRp6cp6EnHKlv=I3;G-;?KuE1c|8O{#f|LO*k^(Lo9vb#bu@ z2?-e+6E1d6!gr^KE{^Mjb0#{kJ&DmU6N^#@(())R$sl&lIlOZ#ua&b#jaK|YxPv}gQaq;r%JtL=k5y?Ej$H!+> zFfeEm{oxgdV~xe$ehLTM$@};3A52W#8!SCc4h$^1e%-k84Fb^!NEGiEE+{An4HB|G z@>^9^-)oH^y8mJ4ax}xX)N@HgDfM?=zfiLafJ3?}kcAgw&w6mfjsN@9am>53nA0&# z85&vIF}6AOrF!G@Pbb6@I}RQ>vdg?MV`%7TN@{8rGI;&{uoM05GwbN+=;p|`P#+&3 zuqpe3NSC{vohZ2Zly)&QbJzB^proYK z_PV~hI>Qg%bHzT##s?zaZ0B$Z9`osi%%!}%;V@gqKox(?@+|>PFyim~`<{uLad$J^Ea)l&{)Lw2AEPw}Hz#iwLYy=4_~ZD?#;r!N zq6ym3BiV!15y!3W)w;KCXPxK3#9^iG0o)nP=f>9=hd~_=BYp{&(-)WHKVYdKzBTq- zWt$_q0euiz(e(5Rm`@8zMPiTQ4pOJ|f@>(UAktJR-*F#U2jG#z&dv_pLLG7ud^x?= zINYAJv@}JF7}L>L1HfoP(7DKZ_qS8KH~D_M(0gDNmibP~M`T~|3HS-@ z;GtVX8__2HY*UDTgQ;~;tl+ZDL9f*JE6`em&Y3{5qENs?Q=-;-A`n=5u$hVF32ePs z(q=s$Y}aXo9z>;TfMyC38?J0wD$^{`(|7*iT#155zfnBw5YjOIGH(F4$*^pB=jbADfOsX> z(62MDHA=!YX?;#PpH;X%uH`73v%nKyaOrG+3a9hRkEq&;@@@t5;p?|hVr z9NrbVmJe+5d@&r2-RBKRe~M=sJouZE`b)tLY;l>0uc3-+)X3Za+}K(> LqOKnD`{lm?)w9*M diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16.png b/Assets.xcassets/RimeIcon.appiconset/rime-16.png index 9c0308ec219f22e519dceb40241f6f3bdde60d13..45a7346ea5c53d927aa5e5b602be6ee1fb7ccdc8 100644 GIT binary patch delta 464 zcmV;>0WbcK4*mm>IDY}KNklwu|yD@PK zmw7O25Cm7D2Bsz=ilC#Qs69UJI7&@0*t7;027fro&#B^6)j5VFUU|Nk&;XL~Oz{rP z0q2ui0AKD{FTp#Y|EO=z1ix+DR;g5y-|rWoR4S!Tr=v_JBY!}qX{uBzDH4eYxI7bl zl*?sJr&HOsEkOBvUW>&-g+f7qEX&elGEt>c5%3}D!Nd!|dcEd&JOXgL-B_(wT(4IE zPNx%_&E}#1A%*dHOgfz=kw^e=yD!sw_6g41nc#hXf(=jIHX>$ zbGcjqeo2aWO?135g~MT0t5uCgBh~A5*H}+Ek}y3A-T-pB9LZ#o!{NYgw<8{pv)}K@ zX0uq9#cH);Hk-NqUI`b#-)uIi)oN_FTi1EZvRo6}?MXI`Mg!oFq&KgGuYdx9fP%rG z48wRB+2`}gvMhx{q5I#D=S8}cEcpBuegfa&AJ%yFoc{tf@gHgw7qD#r0000Td8cug|;6YY~aBg7SLa+UftKPM>?QsVZ z6P=o{#IdQw&5dEg7Ew0@jT&GJ5H_7*>i%@oDFlhh44KT$MNMYh*Wx`?Y<( z=Y5~|uZ?A;%VwlzrXmP3quApvhxau7G&}~c&;EXgg|`&hQ>7w^@k#wmLUz5Mfgs7( z1#hKR$-GE&QV3^x$p`RgNQP(x$zKwcS#Ax`P#>rfL?<@U+lQe7@5ElQF(e}wfPmns zS3pI5sh6u?!#Q|t$zn7=N<)GW&{#AY3W_Qnbz(7I8m{%%1ct^S+8QV3(hZ`OOc`1r zDFAigq=_R*5?$!PdDhA<1UBH$L(QbwOi&bIHk&95Z6j%NAsRn0Xi(w(bh*1IZVo;< zv4Ez@G(kin5j+CG9k2r>(YJlA=j+63R%t z;IF$6YV(p-+Tx%o%RNw7H$1Dc{|e?g+Ak>~7D^UEYz-h}u?9mEfuswhprk;<&^t@a z3x=VKMO9-(4ivkc7*vf50#Dm56bX0>Y4Ve{MJB7m!kQdrA7!%n?Ho(ltgM~%P3?C} zTv*TT)PDYd?te)UV0*E_hvw0nPA>}DBd9Q4b@8640Bhr`pn%5efMz+pzMUASCl>Hn z{I&4V5tyWn1OO!chjdTERLQSJSOvIhV6^^2K@re>Lhr>%CB*$AW8d!T+bK942Kq8F zh~PtFlmQXWF9i;&Z~IOzLy)9_Vz4NR zdrs!0o=qC^uHR(eu<65wy{6Egi19X(>zeI;_1tft8G3wO3kt6v+mn(!+@I~g`c=2; z&v4D0IB0au>aRfvB)WOWw(^qUWBAQ!FD%$=_`t@rfhz0tg847+UvYLke0k0m*U-Us zAv39=cYbwF^QCHX_t>I+)%N$+E^f{3I~P1SGGW<%BCX$8{N?@~ygB8kBX18~zIr)( z$bYE)%<9fOBeEE|S@3J$;HRgzA1%$;@>cm$!?m)EMt9oQqVz4Jb7mIGbW7(&aXG$o zc}{bd;f*h_oaWU0(}SaOG|*UHa{dVOL%93316O)*=jk!=Xxn(tg`j;EzB71udEe^%+0(_c8vf0M}5xhUledK6uepc7YPtuYHBHLQUrYleNkM~_4 i9?iv%&l_HMst?(@XIi%ZRQsU*%PKA`b$2YS-uM^6dvw|W diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256.png b/Assets.xcassets/RimeIcon.appiconset/rime-256.png index 05298048410d74b00fab076863b9c765f82f1d9e..91cb364f58a2e2cc5ec3025f1e11949a3954f945 100644 GIT binary patch literal 7751 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 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-32.png b/Assets.xcassets/RimeIcon.appiconset/rime-32.png index 61b91a9866b6682f1c15bd49e2b26b46f219f25f..7aaf7b5f62f6e088d8f096b2e4aad1d3815fccfc 100644 GIT binary patch delta 817 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 aD$5CH)dsgcXV1ZxNd2tNnZnanSN;=ZJGlM; diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-64.png b/Assets.xcassets/RimeIcon.appiconset/rime-64.png index d2d584dfc7c61e24ed08c0221a89bec166d96a4b..ff3b561b191024a2f59dec976dc45601ab64a016 100644 GIT binary patch delta 1984 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 diff --git a/rime.pdf b/rime.pdf index 502222e9ae99f82145f92442411b394b5b459eac..f285977dabb77c56e81c799ef2957abeeb0f6701 100644 GIT binary patch delta 270472 zcmZs>2{@E*^goni3Hyb&E~ayiYzHA4S`^;PY?v(TC^CWJ8^)wD@V@K_FgP5-_N3FJ+#)z8t{k$TFP3N*7K{k7 z3&OC1?VQ1GSYwAk9s%(WFbE!kgit7KPh^y@2Z;#}2D@2DxT8bE(6%T)D4s8%V`0Id zFfa(?$P5laBKd(R4i*|rV|WnV>9$d93fdP1w6b<*+WGtW{zJr35WxXB8xxKVgX`z& zPjj>jML4s9BY7S`S2mmPLZIS3fo_g&Twf^D9ZrHGAxx+Vk70*&VBsP$QJ(lRoLeBJ$mt+@DyGW;VP+(9q~5)X{D@#h8jg5BUiS0`6AorChj89VR-UD>{1_Xq}( z>l8)}2Ekp4L4Hh(RRD`XApnR}M+y@_Vp}75|CtCSL-PdJU@31_=;BL3Y&6R{K^+t`;JPIpGa$Z$e%hzBD8!)JNW7+c`M z6dVrDVR2pTxnvp^fs7)8*{FamGQtr-#FA@|iq>@jqb? zI1Nc>!r@>vmg)u#0hqASU_XX4FBr`AfHN63P%hfpm>nJ-?qrSdH|Ei?PCy*okxyW9 z*=!oq(bXS_MiSlOP+vG4`VSBZfto~w14BKrCgf1CiwhWR?Gykd2hmN4Zcw%{(G@}$ zp!s%Is1OFoo&w_$V6IF6!<7NxAVL{TCxoAGPyo);Bbez)BmWn2hY%RpghO=ZQkZ_g zFqB^u56%fhvH-qpH$Vi=m*5%>p$Czi&>So{3?2!gc>sNh4xYgtV7OJVJrRR221W&g z0e)L-;sgGfC~)U^pd15(ozVyg*VCWx>J&xsa3YhSYzEdPm`@L-Kp@UsA{F2tz~%G7 z#z+p(@oyn=un;8MB)~TqO#vdDquglWNSFUA8O!98@eC@|l@dm^_l$Dg$^rDW4jU|Ai6P!2*WrS2a?17*%%xY zz=co=aFDeL2OjQ1AREK6Kqq%(l!+_QX$#o}$A?pVQI2$$FWWtW41z%HShfHb+Six> z$1<6Y_;3b|1LqNF+%V7oh-O0hwg5~(1l(DG0KTqdunC*&5E|ixa&q#6(1?sk z>j*B69BE_h=;p|TA_0-OyX;;lLEQI1Fk#tMmZWx9p&{vif&*lq-{!&Z5jgd@3B zBrF0*r-p-h?oR%`o^W3<*va0;3B_>bQT^@0eUTsl(*fce0p!yJ{sbH~iv1rgVRSH@ zN&ByVf!z5K5rA;09U}^d2qi@!Xe?tS0fxjnML59#PWH|qEC=do@5!aYT_{)+95R#| z8WGM74I?-?M)(0DiT+Sq2%H>_h=Tsd3kzlw8W`bjhXuK9jU)QP7{>OVq|Ey&dosiK z$O?eY5Ud;4*&`fo?~HZCGQ%VPIp#=mvBd-in%HcG-6R4G1bDEyCPX?N1dMV6Lv14| zmGKjD(@9ps*B#7e6<-2cj6Glhl*;FnYjg0y)_E0**(=HV3?-A+A z4rF1u&QPboKwkkQAdmw96WyG-E`E^^GS?}JjCN$x$tVC5?LzRyd-w-O+66n?$e42X{)dfW7xRTkvh!C`mi3iiw4@<)SGm*ve zr!v@htd&2`0~}$z)$c=*z)0H=C~_+>AQmDt+?EOtLc^J0A~paY%HlHYOjvM_K%Or? z3d`pcU?Eoc2q=%>?9LAZ{zJ5KcZvYur~n8&f(yixQ8cEBvw!A_ylUoedC9*Y!Isa~ zTo;Gn|B-h>1rwZzNFLEWG{lzyNGO^b0%Y3KeF0890-6iLZKa|gkk9)I z?m-C!Mq0ZN0syqF2H-~p5Z(D(_x>4x25|v&=kQ>hi5(EmGYRFANG5J9cW3`lWE7qa zVhdmat}YRNkr+oe0yUJz1UXt+;TNIVha)&jz=Tu zKmgGfu*FD-?tbFd{Q7_0inRSd&|*{Xm4e?lAz zj)i0b?ch$vTjp}N<`Pdh&es)=1G+&y+?}jB=Kv)Wl?I>{Mpj};%7&{Cor8;U-Spt(B59jAZRe!f*(-{}55ZTTQ^v-Vw-MKWTGxBdCu?PXX{vH4K@DK6- zNgJDgMt~6nJlNi!KqasOfzJQrJ&|CpdswI+G7?RV3bqeN(QRye zBkiGZKdxgWGK9_Z!#VRYJQq-iJ0uEeMW&G4Ad#+H3k!crz*fdNhw-6+023g9>`wo$ zr$`Dq9TGBM=@`ge@}!LiYvxx?2Z8T%Zxq&fq{Mfs1Qy##1rlh^>;NU^LbEvwhKEFiw~ud+=8uK-8@*4 zOfHRqM)Ij)L0k0}1rG%?U|_DNJ0HkJL$}IM5M)B){5SiB!9sz!V4{sg`YvV-0qM&$Vlsyqo%DkbXm-$Qlokkt+ixv|>^-UBVc4yh{?qaQT_%8tVZKy5 zSO|?xBEjted<2hS;~s)>A$a_)C0)_~V&0&*C$msdQUnMz3CVn^61(3Y0fNIIP(wdR zfS;jX01RPh0)@j2ksufh41yR_5L86tDb??>+Y!*rm^_3y0&EP2ZtY9j^IQ=K*x!xJ z;ln_8W_SdJ$)-_Q=H@^b8_@r3aSm_2fVdzI-A@w%|0@c_F=_P7BY8@h_wy9$&I^Y343sglapaz9`n1Sd_Ds6YDA%Kp26 zH*!6G2Ns#FpV#@*19u?22p)Q0Ikn$*cV&Rx@kR@KqW>NPO1DFb(VyFoZoszdI>3Wm zt_=4Vj+o-~WwV5mg3MCQraNO?+8W;clD~XVziY|eBi+N}-my6K@rq9(h7;`C6DgIJh3> z{vk(e;*WyOQbNX|r*f5J-D7R}vMi8Wboa9Oy5a3@9VQtOf8+W;k^J8X!!xBW>4_nX z|AszC@~wzYruQYM?Fd9>gu@io31-{|xr7_jj%?h z$R7#ACrpIn-a`=OyCUQ@nnhnhZbvw`^~l|paj~4C(uXT6bfC*YGZ!VVS2g)oS))?h zBtP%4XFR)h7qlbg4ao1|0l9-;dQoXQ=}2D*bkJ*w{ioa+`VV~-C^Be;QcKAq>PVXt{gTlB+#1$Bsn?Ulqtg&cxl%qVkbUO%7= zO|JM(d|EP-1thh5s453fZg?V9X} zz+u}96;;K}Y3MbSO~1OGs`}YupAMuAvb5Vub;OvO3vmkK#h*QtQH~GsEA|cjah{9m zI^Br#M>V~ z$_aJihsKoOX%sk~Cf+x{uk4>XGlPKv%QF9-F28rzcQeIxhJ@fd5GDH#Ey0gBR_(ly z(6>ygjE%Y$IxYzK+*hqqplxMiYunQ!EH06oD2=*)+Y;uJcB)aRQd>x%D5}Divz9m@ z4fxPfUY;G#r4k6C0ZC4hl_jZ%7Mshmvd(fS_;F`>!H&HzfrJbUH(w7r9krQ!D6WKD%^-yK+ zt_~6Cux+MG;ZMhOnC#@ZJLzq@y<%Hl5})RnXDK2N`*<@q zkrxz%_F_I#Cr-NoWmoGS=UnfVGpmu4&+{s`zH>^aU`YF%`7XgDo!ySXhHwiz7rb|u zVz6FulJ3Rvqao2L1=Srt49B1Or@J5;*gB3nZ*SNZT~&TbRy*r!uY!1pk}O3^mgprb z9Susqw}*gx6_?zbxzBim|8r(#ba#QljR%T9#s=>hDkwlK^=qzsC?_1qzHU8V-PqC7 zKl=Q~p1^6w!vi@;fwliDEx+Q|uNy3dWon{F#-^;XVC66|*-t@o7L17Ed!pKLCef4teV z(`@mhmBU?>bT26N?#+gH)u2m^c$Kqpaq;MMpKUU-8p@ig5rR85amti9--m@hxuR$N zPBmQhNetc5uk0_aBJ69RmkX#!=#!G&(==r_;@mux;DopMgkHC|O_1E&o?4uKO z`=phyVy=`1B<-ZYN<_lJ=8TP)qHZu)K;_jNouTBZY32SSpDJyKd^YQk! zq9Wm)lDQFgY=lL6f_%1%sVQ6)5|@|OxW3KW`b>{#(!xobGpEI*buD4Yh$q{`q~&DQ zG%bCQ+M3Bb60L>hrFZgzZl4y`M5S#L+ObQ=@^+AqwU}J|sUSgOqEEyVIk1q3BFr-4 zrJT^6mnV1ZR?|e?wiJ=`Jb6lFhm1N8rEQ6NXeqqwRQhe0CCCzmI(bUCC+RloZ;Xtc?Ag--nDI~rFAUTUfy|SB_^kK{kpWC ztcKc_Uz%zkHBSji36ulmhJEe`OUUV1A}y_o&|7JnC}(w-amS|ks=I;bZ7uTc26tH* zd3kBMU7Ta=#p4z2F45x`kG!M3- z`I@iqSc@w{US8kgP41KCD^Feo?}?AWwS$(hlP9f&2va= zil)w1m_^S#i8ysebQ=9++o?OJL}g?(l@+c>;H`?>`_Le*yRWpi%gM+=c-w|Ai^|K{ z^_&(F%at&H@>1~Rl-RA*nEMo&)CpC~h?Cn2JK-Q+MvYdS&~`C-Rn6-@dlaY-btAUL zX>AwVrK$7KQcU~0{K+`$^q1RZcd4mqz|3!Nmru*Pjl3Ry$3Z$TrTzmGm?_H7jo#r4lNy&Q=+nXDTP3;cS zb%PRp1eO{)NL1P>DG*XdUQJVbixkVmh?9RE)ZD&b$ks-DD|s}xLxs+qu~j#O-Z?Fj zAR#BS%k!C}r07}e`99RglRGqZY(bW`=!e_3pOH+>+bReV8{yuftM1b3Ax};UNhm4E z7({H_rm#KI?!kz%cQrNPhd->|d6kj>tBWEydd%W6Ofc6*rE7Gwjje?em4 zS57z^o7Q`|>&eQ!u?6Z~l->>!l2n$^29cE9Z}W2Afa^${p4 z^oeizt*p51RM9ITk(*avZQmiWQ&mGw>y(fPNCcEB{h_kx_xorV_S^!xJ9}=p00Bac zRabzguez%#-{;o$)o_DdC?}H2@LA%6$Bx)E zb?sE?mqEYEAGT+E<&XPV71>@r2^L_LRKtGdmgn#9y8QJ=TRsAm+?2yHMt~waO0F=$ zLv;7`OW&?Mjcq#R*a>#~yuqnaHPE{#9^U}&G$REDG38|`GpT!PVyc=ropaxjv>5ia zQ|a4egzv{$o4^K}){+CtQ$oyjVGB-OW?aNs&(ijgiOjj?ehW%v;KdVB-vo#5u^QVB z6feDA7+O?`Rwd6)UZc%MrIPd8gT9wUQ-_kWj4_uj3M?we9|cFh$m$LvE`7-@94c~| zT;Xr>%VG78_tGEV=x*PvVOcaT9_p)KKVN+AbNLuH`URlxMUlGQl!r@mQE=5t_R@5r z$)H8&p7jryiIl^?f1v`KpDzmPs?=BW07nh?y^&uuA{G8nDY-}PEE>Dhq~B-z)DTek zoj#`?e%I_GVxqYqEPLT+Ap3IJSFg|fsSo(&h1V*wzXx8yYafJQL%xlajab+P*PAA_ z+c`Zrl38_)Xw+E{w($GIP^jwctIcBx<8vNYKKrw|PoI|1{M(buj17x_3X0EpD`AG@ z^Se6CE^zoe=xaB^EF2R8`olFSbF!=o8h5CAC~K-6_mySMa=`J3)mPYgI_oT@3y;4W z-|eg@)94r||2^%fi1#B%q+TdVo%*gR{C97>t<*QVLF=T9|&j7QfV z>#BAV{WMA;dC6{Gfa=U7f&DFZ=jbxasD%B_u0j~gy*1M{^yW|_p7QuW?-dJJL9XW} z6gG39*$aP{W#f;xc;S*qMJ1A2xTWhw0RK`_ZncAdf_Uu!{bp{ovHI$W?C{k1jQdhR zR@;mmYm`(WEi*Or@IgRc8+EhftXOm1`?_e}jTR@?c+LHBxBMeRhy;zT6G zgi@6h`b5M;7JS8RVeS14qC_dEO zClulHP-mNn0w_RNVQx!8OiHq>=;Jm~NgFB2dn2(9M-E=bLo0c@(<48g4PRR!Jr_)u zb+27ZJUCR2dh+q)9ig~0wj$Ix@dR<1_~l0lxTH(jwkXcMlD-3t14q}UoHL((IWR|{ z>EFYRAM0a~lHp!Xj1TGJ+VQ^A?!qYM2*)PVt#!jUFO1m@I zU;W(W$F&^`o$o~2o1TQ^FZA!sdSq=%U>8FDpFg0D_!L^G`>W?He7ZzPncWLrms=nl zQ+Q)}t?uO>@oWPJ56Uyuck#Yzn<7#k89lHtxoktnN5x0~ia0r56gEjMerDw$I3=bk zicw15p?m&KGNZod?pwJ-LEaa~GVVF*H$7x3I6TU>OXfF78%|Z5+Ij|-QE8WNF74;@E&XwwvmmxF!5-Jbe89;l2(*QhS z{rjhGUM0t+8A|j#XqYZCVH`*~AdoB3%U~LOJgZP3y6Qxrn&>B{w_~2(j=QFQYwv+{ z&3i?|;F;#9hJ(#rX6g{9`Tl8uGgDO-PgB|*Q^sfaHU3KRN{}9rA3NTED^YRJaov8_ zOrEscBp`#y&TW79Fy*yr>9ge1UIY;5xblt+A4ZsdWLD497|hPzCkMbDUV=8|C_*;X zGx1bHt~{xD7wP~3cGLI1q{l|tK2vw$bb=xF=T>X!?f;~kc$uZE0Y;a#%AhCcA8H)snDHLb~r{pz$= zMf-Z5w|IZAotmxR;f67(6S!Y3yM(uTd%lbtZeMiu=g!JwvSU>ir3vF+)m*H1RTSmG zH&;6BkoLaC?0zysfpcsRQ+u-1AFO}>&`MWo&;0&t%9Rw=~qx+$grlN zi>_5gPDc+e($x!9R5YfJQPm+52XH*KKcr7z4HOg(!J4Y7Y~l}v>{(B%M0ZvHc+cE3 z7`6a@^G?e@AvHf#XWzs~^2OGWv6Z@Sp?BX~N|%C4`%;*iXT1{2ZZb3_+Ad`&c_bI> z#gap76(kAH!LRWR-wz0eoHP#Xe8b~hier{}Wz=VjtR1|sb=FR_;cjWyVa@jH^-3&vRmW=K+hSpvFj zGH2s_^5VxspB72f-j0=9DQrjX#>cdV#&=Pe@b#cik8eEr;QxA9z{E>hyZ1|#r1eXd z@SHOw3>DJkGc+!0Rv7vn-Bq$qVu-0Es)_EQ#e7%FK*^PSU35NNyLe~OGTM`VM5^lfJWKPZHwLG~NwHO)VToD}-`K2|Wy`>e4%RM^ovUf-VL%XtsHVc~m z%vqi$eKX}yXD(N*xzp9X?I*szSbXQ4q_;{Hk81hy9H&$?Ys+cf?9d>uRzMueb{j>- zychC2T=_US>i5SrTwd04bnLUyivnui>qEcak9g1w-w8rf(%v_dI}XAJ%a=-9b@nyM zWW!~;TTu0Z{HKV#Ka+(02ENqE*%{X|krq`D#9c@nvFc*Ts#L5;eQ6?hE$25Eq};4% ztffwc1H26{ufE;wru&BE5e*nR+(5v@ykEF6sz}$62{&uBpo)hsx7HVF2%5E@t5>hN zI&k`8hd0mXbCM-n^}?b1tG*AMpDlOoin`@&?#Lmxc2uv7^W2UuUQX`gF~2{nEv=wP zZdT@Z+lHun=dtes^DlS68J{A(PZJN_EqUY+?q5gJ-&HhAi8e-?JCvdLv4Q+mR9O?T zEHYD&{p3sAe85_4%jqf)0dTbi9HakR0bl64@biA{{OV?SNPDa?Ui(!fRCadm{S$9C zjyL^$z4?2^UBTr@UqzMfmDq@wAJKplVKbBP!}1Gn+t)s3Uz}eoFh7dmB{E_=UStG7GKVqSfBmW5uC7L+afp^-r)4<-Hmm2 z_ONc@%8^n;MdM}TjVkauMSN&!pY?7NL6CtrQKO3=+hX3?`1w$C25Gb=ybEnik7WLS z65W4AfBnnqrDd?8;B54V59~SFTfrUY_Ei0jowtZyS}DKWB^6%5SwlRSfp&-ekrUiJ zTzPomP7_08C{`|p9jhzwSHI%Gy@bemkW+}gk|lj1FDl~PCiaKb=MfUj+^Ih&>+xmT z<)*dBu9A6Xq2#J^`u|2D`{fc>kZ4>-fOW^w4;QP#XxX!`j^gUzmBpfdc zZItw1p;tVFTw4l5?DJt;^ zvY~GyE4rq0{|x>)_WnTq=ThPWu8R7q>5}%MyneOEMCXwgqtVOIsrEYK&1;2@+W65- z`h{V?D(#Uw1=X2H+6893%$t!5`3vF~TxVbIp-;>@AxfFx;zNJ9xZd1VZ<+ z-V1PyJbsvBYlcSeT^SB#4Q?{DGCsrVBQ{GzzL*EVPE565hl~yxmh6MSE=&G9Nn2qqxVovnPH}Cs z?+wp;SMKs4fm+>Z9+Y^5v@(0-aA(m<-Rm%oLZ@D@6S{T!v9;~I-WLY?=W;prDXtsd zdlEK#s zO!NFsB?TvO*SWH3onKnL^BU@Vz?rTLiBFFY?Nu@2P4s~B&OzbW`Vqd(|;B_dpqbsv94-CJkB*YjTN0}FG*X{Ya%_i#EJ={2`%ORku$ zyt-pLZ5DK2Pxf;QH~dq=;}3JcTOUvPKY%6Wkx;i*pQzMiIus1Uzt3dPUQmZQW#&a) zlOET~{LTv#1iG?FJcV=N3r7Sd;f zo9ZHEv)kqbBjc|ZE5|}w*TmuQp?s?l!^yDn24+>(!xq?JKW7}BN69XP`TvOw;T*ro zUPRBOcef05*E$T<3_fW&(|&L?Ep}rdy!kwAFmT9IV|nQ5Yd)p$i}K(ycWL~@kvbTO zzqZIdOPLQ`sfvWgXp}VgdDBxAE&yfbz8R?Q)94WNm}T8hb!f)sh{aSq@r-4Bswr!T zF0^JjcbHb2m%M?^?wc{BPx1vjU8-ikb64L7l!2#AA9#)O$s@A0U_g7b-E;F+7@>sM zQZ*j}Vho*l+y z4i`KMp4UrXMAy%oq7WQx?@_bCdhVi_tm!$z;v;x<623K=Gk<7-8|qT*H%l0bdOYH( zf7$zV_i$@cpu=bTu_GM^;O4EdwHpDM3pQEy>mR0F=)+_5#pMR4%$%tA_r0=c(E(9w zC!!H9n3>>6+NFvIhxa=}w*JumVZnW4UDnvOR`}Bje*d}M z(ftGK&fhPWG`Iz~DAdap^hE4sD-^#u6bj?2}&;8kplyCwYz@NyrUJTWqnszJK*JQD_>># zTyfc*a`K9Dw^2UhnsHTtd#tnH4XS?$CKuXNOc(x)7p@P*vtzGKSOO)6pK;NqKMvay z_wOroDLQ=ako?} z$u_hFoUE-rylc6+YCfX>3tGN^e8AN%${^)fNHjV4@}!~E1&X=``sd2zQbTlR?xM#AEOUrJlAMTE_NQ(Y?G}yT8F0S$N`<18PA~}bjw{;oC@Czz$QTbI= zej+7leu?D$jis@2=yiH=)b2sP_i^n)4~=05rxTTnZ5yO?)%*pMWpqMQvUv4-NPlQM zb?U>3?~QiZwD^R#tVXR_N5Hzgz))9pF26Ob@^Q}Q@(t%Jz)XWHfjMRC_BmayO~>+N zV%|qjE-022Ppmlk`)Ld>1eUGBm zoX!z!-A=WNd!lDOPDvuKK7X^NLjC$sk(&nBDd+Kd_299FtP3&sN-lE366P-|KDu@> zbaiaDt@GFHo!$ezto({2Rcs@{M5HTV$+P(jA3}f5B7J#d9-Q#8?qTFEm+IID&WAVF z^D8UUf7N?0y_8KkKf?zs4BxLmEHHaC!ns!SZuOl?hAK@-DNFa>&AB*m2=o6}n)|A_kq`a`=bn+3h2z8Zi$+ zLP83eagP!NwxG`1PoK>xgU;=6v*z`++Eb5?R;K4Gs=Qk7#+Oc=UTR;hjr!BxQ}0Fr zIvX^v;(IfvvL=YPdf(tLdtAw?Rj0G^*5mD+%{-zq!{AG^X1TL>p@ydOjzqU20m_b> z5FP7U9c=p9V|V!Hd@1?x0=5TzqH#0MI?1OX2D>b_u^7GCQRQscSt2F1z^$+9G;^-5X*sm|t~L|*y=l4wvnN|-u=7mht&NYH(Qf5e zDnl201YJ3YFJLCQWt(LlGe^ydlaW>B!GNimx@F&8v))z1B$dn9evi*9oHCU^_aI|^ zS4^>0d$K25PIoMdQDbgqMVbBpz|zZ(P1Px_b9P|&Dp#n7jkm%g^=r?_pjXNX?^fBH z-%SZQvmu2e*H455Wm9s7TuCkS79GRy4aV2Q1hfZhla&xz%A_w{g-Uy;W$4^FbgV5> zZ;2StbTAv^xJ*psY#v=sdeBHrIZF(g09>hEa{be88d=s7)P8j9u?3=_DxCe!f}Ay_ zH8U((8{6qk2qIViAW$L_{a(ez?}!(ZR@Jx;DvBEg8mW&u3$oc;S=l{bx>^42*F{4? z_=RuBn_~4>FM?wZ&zoWmJ*qP_=p*$Z;h%Y7`SH=s&dqm^cH;L=xE?-^=G4upaLze5 z@tA`Hsm|ZbI-I!0b~;`M-*ugj7;~jY{}?FX3nF2t(tOG-^O5!Hgo=U0&9_e)}6t=RiLR*akmZOOKXcj4i}H;{Q%@ovek1XPtQPnEguk z)yicToH>g`q(4v`b0}?cj$Sq~2I&@uEB=0b*pZx+$j;(h;O#?w+y}3JDc`$L2&}R? z`dvk4^P)<{2O$^gh0T%D<4;G{)Vm>!{&GR5aql$Zs z-Ywj*JKAJu-W+tI`#N{osJHppxwdHbp5bK0$WPHTSiW7hTW{f;Ya1Uw>{PIM02+27et*$bo{or$&HZg#FFB!Y>FIZN zh`MjHTT%nHj&+DSoYb{~=c24nO1gzVQP}YW`FLQjq-~|61E-nv=gLd8{LipoM`~a1 zTfF;cRP@)8^SW-|`!?PFz^701{tT=ns~mVMy3Kn*#*okbBLcLO6@2~us3hr|;N|Zm zHN18~+vC1-D>kf~38xK4E4cka5>Z8|vBYGY&=qNa3D1DYy$RwgF7JYBQ+MB*Kl%2# zO$o>@@pvP=0bVlL|0_dz(7v1zqZ}*F__Xz$|H7+@lhdCKv?WhxCBi?YyqsA|hSuoA z^(HZ*MYXkf&!33?GVIUuo!o<{GlHJ|dgRv=25v_*pT%ImJj(sB_o2**r{ib7gO5S3 zuYOy4&fC$)C_SI|W2ySj#AbK*)enNJPv_v`6P?dLe^$SB%PwZ1f61&CUWIHo^@GIJ z*4L}}9c#4S$jj{RZW&p3ww_S>qMp?qS12vPEXnFV@?3qV-GZ~m$x}JqzaPy%5&TFS zyXda0T{t)46o)E^GCm^?A3r~u9i-ZNd`1$yP^{La645hpCEN5=a@M@muS3tYPD$0E zVpn}1?7TK+mFM^6QS!}&3%lNEkt6nA(IQc>aHU6NL7=ZvE* zn=5$VuWXZkOu6NKP`5JX%>B{)6RYuj0lFF^-Kz!vS)kc|BLDV%(if58V(siNE|Q0F zy(Z}=i?p``%}+Av^%(Dv1>GA@kS9IxFMO+LF(IP_Q3m85Bu(uk8}ZNcsI>h&J(yTte_bo(e2?k3lLpBJ`x}3$ORVm5O}X?W3~0WqB}a#M&O`o$j7Xi{ zMpaP+0C59qVNM;Ed*b=S`RI)jlVbvid&W^uMa45l8AYa_;4o*@b`PPax$k%F+iTFH zcU5<<1MZ2Uz(_%SM?U+ioo_oV?uG1{QS>cbjpK8SX!bmApv@d>AO*!6w ze={OeF(C?yp%IY15R4RB>Adxk41wbZ$9Lupk^{DFFzBhT)Z(iM75uw5jsc}hj0NfF zwfvk{HMaa~^1Zr&{oV!VFN#Q;TC;VldWHrL&gJaAmAamhM40y5Q}IVRu5M6y{VoCY zE@|723f<)MMB|)4v;%{Zu9$t408gP?Q_af4JLj`am%i2nMS9U%aFtZZ0E8RNy z@L_ai2{(R6W$}{vjmG!NR3-VRqDAV$U9&!^B?{lWgmRNHkT4%DGet5>$`zChvIO4A z8@)OEW)NI(_x|aJ#Pi*9aar*@blrS*UV2_)E%fr(VUfA{v+&omymA}IG6jHYWjyUt zM_A#3mQU&J-%fl`EmjG6c=76OtUxZ&G0DFFaPPv~Xw1V8VEty*r+2fXCf;W@APkDJZn`%jbnbbo6%k&OG?FjoBiTH08Tvu(XfQ z6H~im@aj_2$x83x?F7Gh>_+j`tZy4ZAMPRocJb z!{YZnV;bx9#134G!A%0`*~2GOvvm@8^JB^TM}yt!=1j+6mGKKt4=Xe;3GC02M#WivoKT0YC=Cj;QpvrY#-^Ir}SRBQvBwLxMR7GcAoh9In~(U3>|z~NQtbq zDpi%yB7zdQzP3#l$=20BYiP#(B5x4k`|;1q{u6SyeNk+tWKTh#Sc1Y$H-f&_?Hf|@ z(5o^&*qFm{BY7f&+xH($)czo2SyplV=kS|y`_C!WIXa=ogA`zwUepRu1+fn2;ZC=X zi6)oYhBJkWGi7&(y^}qU`MvEuVto0;P+iLF3dRg!>??FR^S<2LsN1R(>_gR+5+Ki*tFPV{&FbnDRXy;-NKs>BsB zs;ED=-1G)d7GMs3igAEp2L%yv@0|9(!+%1ldIH{ED}4TR_EovEs(ixJH0_NnwTsdh zey1Peu0|!`c|7Xz_`aD76+fT7JcJbnh13;(r;3Hq`z<()S9 zs#;G3+tfX@W1z*$LkO~OR*?7dxL+&A_Le2JdXuh-5|8YDrgvdIS|aS3Q)YDigMmny zNR9d6YHgtR^#XOC7I92JGSzMh{$b=QOYMr0eiq@2+PYfiyYypiX4WsJN4t z2I$=QESA)E-#QySxj#EWK4X9CfrCvF*9|1mrpGb7p{Pz&%ZVoTFRcQho_l`=A5fQi z?ml%)`NPPmtXcIcgL`(~ZDSJmxqEQP3?L)Dx?AL-NYzch%}ndhUgz$jKdnsIWJc9W z3L7PNTs*&f@p7E|m)((qnV;fr;oLi_2V*t(D3|)7m&tywsR<3i3(tjoYWnt0hZV4E zBho!%8l)Ze(Py~6beM>{e#eeiNp5EH>8AI-X|)kQJ+b~ed2{XR1vB>sVZ$RgKOQMb zIwEl-l8rIkhT4PBuy8BT?}sNVrz)Y$A(9C_RabyVWSuKSGX9Mbh{ zNe>sJ#1r?Qy|A(^d`2Mq;`*6wy?p~ZXBfYb?Sf{ z-DdV4-W^(DnhM#bd2Z0*UP3-wDOk1G^jgfKRNid@ZT86&^yKgnw9j7o1JlDiM2>{q zuN$79vlxPpAnHWBe`Aqy9MEU4fm-2hvCvzqbwv-eukF5`wYPKjvHeiM^bG(_I15A4 zP*i$0uZ>EQ1pf(K&GpXUCQ*1tFxA9JPMJLz2GN1q6I({uUQVDASVIl960*vhCH zk*dN(LVtvo?-jj}2ZXWV@Q6!I zV84Coz8a+$qc&ncKQ~Ed#@^+Bx`wcNkdYefy#oFEy=YzmC??oF)n;n++fo$60lpT@ zg^z!G-M0_>y4tS-t{D0Zmz}CixyvG$6v?PO?>cvU{l|+ zOpySs*FF8vYg%XOTlTu#7_3J`I$u#TMIK+SDl;u*{;cQ5$;xyTJ|?aOc28N|Q2T{? z8@atL*rcBPTM%ofYji19N#|h!w%`gTw)?~s=tcHERf)MU^RN2tEx|)S!m953>jdxK zBPLqy--DS*DhyGIvbN4Q5%p|teW3HuNB7%he28~YtxoOAX7#efTio$lf3M!$g|%`r@F_cVPidb>s1K)rL{qF9%)qgR6uF zu02IZwx6kuGs;!^%5M#I5pVwpzT5n|7_402sX;lu``7r>3>Mb1 zenBQ9u(Pi1CKSPMZB57Ez&M2>^kTK{{w$h zQ7+s1e#c;lV8vJI;$XQPBZQn^4J?=9g*(MOQP7>gd`cs}}~3 zTYUedlIFi2Vw@wQt@~Bhrt*3jRrBF!67okW6*jqvuVC4q4b)DFwDLI{(AE5XAWd_< z7T+2=es;<-s zL5L88msT|}vd-fF0WUz%zrdPn-~!CV`k|GA8n{y|Pjp^ykZ<5xUAb5*F5slz-@>NcB~MBTL!MWw_8ORxElpX#wl<+v^O~l+@e0CV6Rs6fj{`+lK zX&m3*Zy7xnrjg(D`Bq8G-3qYY?*)YNjlgoWp`NY3qOfUDVA+-D+0d1^E4M}&@3N?D zxaihk5n;^sluQ9K$iy#|rZftW6E|Q4QxE zXK`pFsD@|pb$iij`21UhzGw&=CwT?gexuA%7$5G%7sew`zQ5w8qSxgSu2RN}#a!5w;)6@nI z@#Jb!RD-j_hjT?aHg&SJJNEw|3>EmxFw&Nztv%NU12JRcP7C14-c#68ZFmK;SZZ-- z;e&e%yx@xV_da_pL#^`5z-8BcCsZ{SyxmcD>@_rh@-|Q!$*LAT&{P;Z2;+xFEuFKH zKBe+jY|o%6Gml@4Ler_KgQ$a1!wy_f4KjTaIY(u!XE7PDmcipSf;dSGwMH;5Qi7K! za|JpmohFNexgf^@V|tK*-cKOS1USw@E0HAMu2X+>E5U?-vZ6Tcim^fLJGlcEJ>Zkv zr2q(jy;BrB2%Pf#DVLDX9R&P{hA*6RjQ6BN!ug^u?|_{%%lI+D12*6s(}zdVU|sq+ zgiHf0@oZ~MC6Xj<(U+c)#f@8OFh!9#7aM{^xqr^TQ|tvpH{7dN`;T=qQ6(2C6P2E; zflSco60Az$dom9k;dz3Zq7%HhA(3nFZQh%IPVq|ENtY?jk6HifPsC%kx85l|A4Bd< zah7<4MnLlvH=fY^VqB=+fpTo8zNS2}rIaCN``9G~Udm=m?909yPuD!gBE$jox8<@u z{K-pOJduR+MuT{2RGySe6ZFQUC&ee|iiOO;7ZMs|(RlmLUy6Z*7VT0BgH!L?wd{s} z5`}X257}q^e;XCO(Zk^>U(!Uprxc8U@$svJOG+^}< zNsd}bOSZy-pc|1YnR^9ml3U)=ix^!3{+yR#xS{px7`HQ^fI|$b1fp2xX5zp`$vndY zm21=d=oHvI75qnvIV^h zo4{{Y?@8uiK;|lfat?wqU%n-vj zqj2Xx_B|{9mv%R~#5}rmo{FoVuEuf0hZN!+E@TbL;~4iKV7M=FI3ay7fWx z2jFBw6MO)j+)?g6nYkgI6Ua1wK=E4tm8F&^^|J_i(4z40|A+WD;-sSun#lVexL0U% ztMt7#>-Jt?UmFIkM9Ua)m8y<8hL|9H8AkbLWefQUFvQo5V^3HYN=!~B05bK8YN(b` zmsUGeG( zDpi$l5yl8WEn7uZ_e=8l1zxG+UPzlc%+hlMUD_LbnZdZ+Lxg3CR9f;xf9iZwnrz@i z4%7TX0|06_M1%|@@K+_HddHJQ^fx;b-wSGV&K@~{oA6(Eluw@}NhKGJ%ors&U?{QE znAfyi`v!QMp-5_CwP@gf{66?BRiis=A=avASD=-ehZe|wv5qkT&>XQCk-u)Jgx;FL z)ocitk7`8F)-mM1d7~9{dSp1K?~1trs{&VuugyizNybhqsDbe8dcQ}Z1v(PT^F=9h(4tL0oxCrSKTVVmLjJ>aklBBm*Mpz@-I6(uJrQ25q=H9?Ll(S8UaZh;6` z_qjWm<{?3iQC$1*;UHg1qp0!`v8Rq)5JJUR8mKo*msv)gv7*IYgm14RG6W$>f=3&Q zu8{AsV?8X#EEp7qaOk?yU&nRd%4wqr+(59CDL&CPU)=$B?62||*xLpAqOttjIuREA zy6N``1sZD(AlJ`-#Zut+0Aq_X3$y6I4n%}0^B}Up$Qt04Ls;0Qlb@j+$P4t~X=|3t zQf`^{Mf8yiY=UC^6y(cj0V0GDO{QiO#c09~!L=F}zfg)>wb@q*uZylU4ouUU zMg;(VG)fM90Jr4<9EJKD004L)uKN;QNGkU)istS-Su`Vm>@vyUVJSu2)(MG1tefLX ze0k!o+b*{5NvA^lVP_OXc=7jh z`?X+HB3s3)N7`CTZ(E3Qt<(@#C8C8M^#Kgn)MGEvd~!5!$K(Ro0`11pvRk zpX=|2Y=E4c|1HwCvyH&**V&kfBI-iyJWZS*Ute~m3MK6#J($I*X;vub2Bu^SFtMTjaAvHUJyP>#D z`(DcpVoet7qXGCW8eXQtyx1#0RR>}z(|M=GX`PLPZ%vri&VXRblcu3@zBp7P$&(lR zjxi-sq&is7MOlv<8Y4J4ZX^jwl&}teX{9EGuMZ)(NzIJF74zjUunnqF7f^eZyro)t ztrmSgKwT}CFe7#`**3)?(sj=_G5Ey$57#m#@qWyf=a8`2aI!>^ce)IWrokW7uW^;G zej-%8U$o=KIo7Yre`#!)v87J3pBI^C)km;&gZhv7?J7Zk+0xFe z`XR}hkk!`_)Py>4A~(D;O#9b^u|U^JqeK#Ay(aJ7!6u-<2UsAy!T?`RsBv)atd(Q~ zhGrMx+!>u@Fq`AQy_$4Vxfk-jo88|%jJ-w7+(MvNlPMXyo+c3R6}PE(H7=z1T_C0J zK)6^lzelXa4lWq5O2!PT&qb+!!WXh%GYwXN4klmb3RN%yxRX8O4NalAV%S5hPa)#?T`KK{eM6wbr z#+-l%Jo6NVMHqfes<_5_m)$tg`& zZF!=KE&@vh;WzmIAe9z>YTMV^rO&Vdl+?v~90(LXwAOD24xpNpk=598(&5TvweFD~ zE`jd8{^Ow1&PE5lp^=$te+@iRNqjlkKuulSJn|?Af}>@ZYOG@!0*%GN$J?E?5Rw)ZckAhd!z*NI#j7!D z1;NZ@LpwtSM}iT5OP4)JfKnItBEV#+j!Z!NF|bODhDw0%4_AE)uzcE|X6_BL%s}SIv|ufdz$wkl1;F3!dAGLoMa?eef82QP6?SDa1$xifQb8jUZNfu1B=_ozdHe@dyd0@k)SB+y3A*i6vopJie5UN zbt@rm1#?xX08HxiEPH-5br;2LP?uy4;6kwnuM;7aEhh?1^aB@g+es^nx%Y}gW*=|^ z=`uwNjf0$-t#Xf0Kx<>R_NYevC}yv?(HcXt_C_;4`_~sA(moLZ)@r}R#@%W z&Q$^44gm6h(CT1kk0J%gt`h5j=uz60WTH{i1@1q}xxi-9%895e-)v*+eT$PyOwI9m zn$YC4z%3ZdJE!M2Ipe zt~i)=jW0h$DaGs0Z(}F|S|btFXo2@`%8;M7{Rd8BIC>+L#X8HtYa5n7Ha1~^o)dIs z(|=J>tbvmTK(9izE}9K5n`{7T3#VWTY>{tXj|+hf>`~EhwY7F@F|6O43<@!>T#TqZ zFf@{XddJQ$82@}oq--JR))`Ryh7Z`giRM33X<4ArHHPfh$en4D}F;y1ZNPT)S>DJVF zfNkS+)9BB$19S~pbmZ>mRYJ5LBEt&V{0H~qUva1Nev);gcb5&;i!$Av4X8AKLdrf)oc!c3gXM73jnTm;pr|eyK6ALa%7Wge zYamLpyP-w$lgfh9+?22dHn`$&*FU4XZ%fcE0;mxJa8U9VImmE(lb+G3TSM9-xn4Q zFtVYa7f$uFwHysRN2&5@SpS_rQ(d5kMmyI@gTQ}ROfTJEpn8Jl?f~x}sQj)z5HU42 z<>6|mlpFG@%a1Bk+^`6-CP1G@x63LaZw3;}a>P6&XW!wXCP1=PtBaL-q_{6)pLl<~ z*ehY~-`;eD_0*aq5M9i%;h5fkWv@VHJ`6>tqoCK8-h*&YAMf)k6KtFt%M}ZH?HZ@2 zR)Bn$+Y%aRUGEfhrYBxByJ%f%R`q4Ks6(KH6ruy*3Ziq=9~zd%N_Vdq=_zyGJ0}W( z;N-(j*NUo1L}T16c+LHDEYiTfos4AEXjFJyvU&!QIe%qZ_d>d+P3eMv)CWkpP4dux zM4g_il{v{abv6Pea<@AqnSKhBlTSOr`Pa||ndoilt*30uLd8KWCeqBDg8R1u8m)Q3 z%$^>A5RoqXI>OpABrY1*D{3fn%6)O*>ja*jSx+}hva|5-CyirL7*UH=cHqzlKz$~o zy)5iQdZExgM9l~AI@Vr)a}wBRLI+Cg&@U+E1G*fryH_@IKdII49&aV zA5{m3)?GsKG{7i47yCotKy)n3Dg&+#QP3ia91+5-v4(%E%_qkWV9Hgb+zx_y5@s~a zjU=bUoUdS>xsIwgAEF}f-lW#lm^*wH< zJF~R=HOkWuW=mIp)M9#fqDjV`0~=H~cprzxbTI$keF4@t6|zwE)c`@bs`KW{?{IBO z_sSi~;6}Y*o*L<6UC_Jfjp}E!pz?l;vxrSt6Dgzq8A=!!n?=p73^o(dbq54( zbTPmI+h2R>k+UGXqy2pmGXnROM^m`V>x{8|&(|t84+Hw6ap{&+ywzd)GSRux78$6B zyva-lQ1pBJowA3{xQFlh+uIA!@|Hd)c3Uc-XB9t7BsDa4%&J9Eu zQUG84vJxhh5yqIov?}IdXk}`jny@Qoo!7W;1gFX=1=Q=)d(I~_CKvQA5nXfuet#3X z7VD32SvcuuN0khD1&_&j)BS^!Ie8J6&@DPSlMsXP;qpV2+%3rCYj!&+?XlB}sNZ9C zq9dk%B!D$RFhGVy$tG%Jbbo7}pw~z<3h@Lr^fD4m7(vj<9WeRA@5wO>EnZ94g8_bXQm(Npm$ ziJqb222~rEd?7WqV4Q0WM}-?E__XAaXoHh~@tqZZu$nW@FMttaQ#Ku^+MfkPKvnR? zRN!ZSDsXJJnu>6mckl;cG-sdU)RN5Snw5Yu4oeM4;G`xkKCFrekN_UvV@)flbjPD2E>Z0#fg_--)QYOxaKDsFud@3X{XS^Vna%nF>@+J)|VQy^%nGP)kp(i>C1_lx!!u3dUZ6P zCE^KSz5uz(<7h}HSlpMn6L9=dq^qNN&CG9C8bPciacFW!Y4~R)>s{9sxRoM9ik*Ir zyxs-fv~`r{TX%mGO=vxL_2H)0nDC~5(OE=h@d*bOU+waHO-{xDN>cB2v@#-bkRqbk zqaX_g*ki@!KA?8PT@C>BQg{)z5V4Q@7TXdmP>Ac!U4KqIZISdD{!-4qFbeV!YCD0= zvqT3gMmUCvnfd$o9@f9f;G$D=(J>P7%7jD1?gS!1YOpZjMHB3zY9O%_W#ONH%L9y@ z3K0{GU}wbEw8ym7?9VEh*?D)c=VBk6L;5hr5gsuml8MK4mhTQAO~|6-PZl!CGQEahpmKFKI=3mUuyGzb`so@o8wWJO#<6R(e+ zXi5SZRII<@4>l@<(n}UQ!eGNQSc7M0aZgewY$aJ{B7-0)?x9gZnbaAYWT7%89!Z9I z){A)hWjX^%W^Tz53&wd!oq*(W@*}J@Rt1!im1O5M%Ya`Me6sJhZ6Vx$Zyd-b7MO=l zomJ0{r6q(9$x^(rDT-C7#Qpk7nR|`#A=j182d$MBfmZRvt&o=-f4?uDjk$gV>v5=l z4J>!X195lJ_5=Ugq0`{B`zMO9*?_Ud))))tm%i==TiNG2{g3+_$!9;n>HY7U z_)z23o=ss)qTj$_Qt`95sieFN7I<6PR<<1A`&+tmGV7ofb zXsZ9ffS$6<^D6R3{nsMvsIUYbFLUyg!WM@4H30utpYP%TaWwXj_Y&>?MS=Dc^mX-T zF4)pv9>5l}^RVw!^)I5ZftP-sh04PQ&y$xnBRlR&Rnn@3=R*L0qy4^ju2)}}Ky#P4 zZuU0X#~@m^(r7iC&3ZIWmMB>=6P&uO(Pb|1vSy{d3e-P){PxOCmQgHO_S*tt);_n) z0y%+@_V@bC_+~Ksy|Z)wA1lHN*($qxse0pNAHO~4gcS{S`*2nnT2C0|@73dw%scZT z6KI22N-(zZtZ$Wn2L2vANVBH7RY+FIWHn#JS#@?|%~28zsi|jskQtKaRw-*b)Pecd zg^!`q@fv@so<1(8C#^&&e>ynNmrxS*rqd(Wpf1RbI=u@}QJjC;bh{b&q70Sl{wMND zyf-1V-z)1Y;1%5)tBr5F#-b1lPgMjw`%O!>J^ML-&lPp8TiJF(w7nYuXID7IWZ8CJ`{Y~xkjnl%GXz$Q{=VsS+NojECP zL+D%hcBjbfwY|!~Eh0x6AG5l>J7^yF-)7fTxF|ErzhH>x*WBoGZ&EWIml3d{UlwbiGk4T`Im0F4J9n(D5b)iqC0v? z$1>N=*V2b(AS*3S^RXl3%M#MASC1e=KI?RR!@GA~*+=}FB-#g`h1??&9wb7`I)&P# zf*nuR0-5VoM0e{27Ty);b&DMFiNV{mUg5gO~%}byrX2u?`i{SZk*wTgW1Q> zvK)=6a7FPVN=Ks5jY4Uj2BqET8w#g8x<9?5 zjtRWOcIZ{(rLYKf5s|}*$)Fxy%?62otthCHduksG(lKR5NJby5l(Y$qA&j{wau5rQ zm=&vGA^k!t=ns@|F|-0Y8lpc9?U#nMzNcgVfJW+6IUn5EgPT&7(|g8A<|VwE+^rbT zG_#n$g}}E;2~xvYuaM-$CoWN)_R*rqwxawr7O61o+af{8GoX$OI$Nq52k^3g)Bf@W zu1i!Ig5$m*W{MV@VDJJMhE^Gge!*#9FiWE1i6B=PDq4&tkP~LMmf{R0MEAjC1urWX zBV(=3pjJx2hn$Hr3ywlJGb2Hb>u5+qLt>H;L_({{&isdm7!pEUCC0eQv`1fNj=P?R zMr_i|a8veAjUp-nv&Td}Y)r|2Mx2^B?RHUk#(smn*g8SA&k~xG>5+EP7d#}+0SjF$ zH$2}s8zr>KcNner<^#@hvz9<4GQQC!*f;`)lutA{C`f>^9fK>8N@HM>XsxhbawWk8 zwTU@}KVDKZZQ}>*?!aW_33a<9c-P{-U36gN`wvND9Zp$yRCvA@`0d8%Qp%P(*jF^l?Va0 zpcCGyV!zf8z-*vojfcR04!||2zJ)EuajQ-Q@{&zRqw6>e>!LSQF7VvIiT?$B+-lE4{pPc4jI=g}stezJnYZU=OEwQo(io1_nLDNbQR zjVY6LzyV@t(pGr`Sk``DdlQ4<>eH25G1iAiU{G0CKB|xy%oE(Q_L+O|mN+EYPP?L& zN~uzmKo?j$Tv{4`KMi=?)ElR8Qy_J5nyfWSW|UOsC6nvr{^(z7MSWPUk>*#D=20=} zWVO?wqa&WM9+W;{s|wfZmIyDU1s<5*ni!ycR;^1H^_VH;|@{P}k!eLVd zjg+XDxzmV|XoMb^y$|m5iOb{;#TN#RY7*4n1vQ&sdT3C8O=F)tsHc+?p$5*7SyJEA z`x5ocOY!K1kpaYRYL9AvAVNt1nYs}^R*}hSXIY(>Dw-cWXzR1;pSXcXsgV)x^RBV$bu$s*hqbR6jG%uu*7pS1$kSaM{Z+^76uDHcOAeSzj15VVG2{`b_RY+%hI<{kqf ztNEcft=Z7I?JwfPV5tFgQ(u$OvU=S-U8z=on+r=b)oC=tcNPPW^5r}ki1&g>*)uWB zjM5a~G48>4|BP(QMNZ5sKW{POE*A*Y=jw?ZAL zIkrxIB-Ta>h&Ig<8KcnK?%iw zy*@>pPn%*JnGOUHV7yKRV9MV?=1oaAD=EMRx~ACkqgy~C)?HUB^%{(U!Qs5D4D=CM z;^Yp`N-klMJ7vfX&x7b9;08I`w!mn;s%^uGCXG?6R2*uarZoRWk{g5rRlK{VB1@eV zmr`nceU-0!gMQUL)FnSwfmY{5>G;?vpYfUg~*ztCK#N?4ZrqB+=;h8A%5_A2>ZmBxTA7Rpd1gpWnbD#p?@1z0zct51O4B1?N1` zr`hJ}l-GTe?Wb1BKDP%A%E~CJ9;>*zm&PK3%{Wv7hHD-DUI(sSXKsem$$p|Q}fUKkR80>r#?J}f)9@5y+CrvQeC|DEWMK~MZZ9#VyG!wie!f@A5Mt{kG zPCNtLQtQQFv)_XH>5lbB;WmQ))_}Nu-uyMyU~!f$KDa)zpB4<3RBR zZXyZhvnoiRNyfU~U*fHq(A~!DK%jMar{n5gf)UPR)Y~nwtdKy82tgHpAlr?*&Rfx= zUP)UQKs~pIGRFYwMyyu#aWQ&571>1_dP(*WG4Z`5 z@MYWvma}1AWmih&7iV;TbTgs;o)V{_n~&Gm%+57y6JAEV%e%RhCF;qAc7^Z`X*)A8O_9RY-vt%U2pyNny6oDS1YQosOBFzo za-c%^W%65rG=w03=#8O97mF)fa4h#3vV8WhZwFDSGT;$KO`_H%3KVxth!N~!2ScQp8jJFu1)E}~|?HS49`9nwC5Zw+&=e*8*Dig+v7b*#MqykUNp z=s{^Mb;NOL1MC1$*1CKglnjKu$x$aT#NOn#b}Wj2nGu`Y07YA(#D3)5Wgke{~nJ5U-S64{EShkn(XHXSwx7>E^nBFlRIFbkgAfuV7m1i?KX5>87;`>%oXh ztBS2PqmH#Z_geNBkZnmfKUltFtJTgf z?_zZvcH3q^*4Gc`ggP=nBDUCYEv9u@0Qkc}1dAG-xh0=Ft23k%&mk8kiUXq3RRjiq zC@lvvFa)+l!zWEn8Y-@pzE%w`$>-Q$bg7y!ZBkRu+At!rviY<@l102xJY5IsA^Z&0 zg=D#8EV?QdF#=+4P%1ak=Gb9k7O+o2k^xyN43V5zqLeUD`(GGO7a`E1umtSEo{6g< zjAciX@*Uyg!Z{9-1uI@w`PHF`0urQuB89JwG|ryVd<<+&u`tEEl%j4`DSownM-#G^ z?)5WAbRvc6uKO&EBk=QC@l)5%qg4eX@L3T@*hzCVzap*9q$M`Zl|WFiZOuIf=sh2z zfXwkK=_yk0?HO?X^0U)7>+Wt2N~IUsTO+j|`>Otdcr@4**RT0!qm8?fNLyWhFMn&E zDKjg`0=SWszDrurAqsGb2-W^(fxi$Yn(yZ}zYxVQRw56E(JRsSa%2_TF-qPmZydpU zofnOPfN@>%;$SgGW?Z9ju#NF~M!ogL)(nt{*jj^LSG$;G}@U>32^uLBx&-ySxgeria)QuI*(|n2vHF zAfD)YbAK}dOFq*qKMdy>zVw2MxFK}}h667H$%Esx^wXos%0Fb^~{==aNdl z{a|rL=o-y~8kVRlC3-+TwPqpRn;osG-1Fqe6n3m7$QpcC5p~%+C~wn$z_6>xOa|6Y zs75e{Q(5SqgeFM>gZU<7knhJLipe492v`hQpllwri(uVv6@pk!uY;;g^tpjE1rt@# z+cu%fqv7t+!8TLjV3g=6Po9-*U`i2+2BoN%+Gyx=hJXcf8KXc(eK;y>^AI!b0-lC? z?vVXMU4)#gAgz{^41c|Uo~}nLPz+hYunuypI0%FlSg*kTy4FIg{;lg?o^`=O#N3P& zor`2Nu#93fzLfxR?ylRIo*JuE!kq}I`VVVS~&w>$Ix_t47*gUq;7gr z3SS08NKu0h0Mp7NXrebeTzng=7c|dDyk-iCz=uNp3bSK8fgnKjO{B0S3QYXY{n)I! zb}lMPNK0X$$%dg(2No;MEzxR=pg#)w;cbnxFlbpdNvF?Jb##B6P%|7il21rP!(Wi_ zu4qrYqK=598?-zz9mrG+H6vT0rYe{nu!j7Pr@l7mc^u`J`bkwAlww%Mv%?>%Zf6}P^I!jPo3DV zHSB;X7Pu&+Y_xi|(*y({SSt|71yauiaxP3vY8~{3lPq#q~XxM~?aJA?_ zG=xUd%H>fntpyDiqbGLHonmFV_raZrh__@lzJqZh(e1TNc<)Ln3fWTpiYbYuJowRe z8W>4Ut?3l4Hh>LpV+x`ZW5~<^#;NhIx#Yr!R;g-dwN0D>@J(jNyVTW6RE$F5rtSg& z0|&s!FG@;(WCU_(@p=X{4F&M^zxrh*3;^3+aR*RId?rDaW<~4|uWzP+3&^MctQ})w zLbN(?U2)fm7eo48#T7j1!H6pls(cP6Ubn(W`Zk1y;AS{ugRnS4vjg)-51aSMfDf4b zXd{~or}3XaDpb;X&#XWfs<*X2%FFmM+11CoF8qazIQwfB!pt{eDt`{(v{NLWJ zOi7fK5Dg7^#*|QO`9%n2HU$gXMdr4oo zIyZxVK8{+|XAVlSxn;J{Yxl31z1=mH`JSrI6j^nrJ}G7@xfDO1D&A5vk|}{&AAUy` zQ4fJI`(qnSdMc4ixi9sRJQW@>(? zMbFpiJ$yv~v--|E?KfJtX!oG`iRsmeOWX2)X%#G#kCs%Ls2;nZlF21AEm1pwzG*x> z@_fn`Br{6}yfsF;Z&Y0aqY}`p!E_F)kBvOp2zdk>CLJNXM(XdGOSh;0sa2SSg*h&i zwufk7c*I8`3AT`?6+?`GSW;Om0FUP$I$-ag+(3wIPut6M#ZsvRwbv+^8c??)W=V&C zb6GfNgk%`(hk-8T5G=-NpEda2UJr8eWB}fR?IzL0_1xETpo&v1xQXoXAVSqsb{~>~ zyg~qNrop-4&usOhYkoq-4;~{gcD&I&sOdvq-1?C5L2Noce!q_PlHj0{dsfU+7z}tn zJ|fDn<)*b%9MzFl0(1-`&XOzC#r$-Cq$zD>2N3d>(+9AA@$&kr=dTstTRYA@=AP9Ex3~2mgr<=I=;bR;UxzRIcmn;epmo1 zeypR4WwQ>bcJ$*bASZInya9h!^gCOO8jZf=amJe)^5M+_)2kzu>*QsB@V0BaxXoZJ zJ%QMTw?EK2H0%CL_haHDKWKsB!~T!mZtiJ)A9N~c{41rx ztUfi-qQoVTU{XQbj`frdJff|r_S0Gpoy!Xh>kGvoqmwyQ7FQ$4Kn5J&^Y2#4_yy+ARWRkC&LMG zfhHa_xkCh;R_aBw$W)dogC;U=2t^RLLvZ+KymSV}W_n#&Kvq|K@UdxoVt?^&SwnKuI0=TXoj1ycW^9I2t_9FmvG>3eCzq=;_A+& z7rJ>NGhj#(7lI}Bo6Ll{Ook?e%#g%XWZKmxHCPyc73RM>=Te)Nrq*4&QLOh5N;Um~ zX~4HDU8-g8wcbI08?serLt8gOx4-MAvWKBge#T)$p~=SlRT!q*ee2;LXa{U6hhd5} z9At?ef{3!_-k1TgTf>e8$XWbkt=Ckfy@##`5r6Pig3vdz|K)8=8T-on?=qU%j;Y`J_-c-E&lLMxTNk8I$*`N-sN+Xy4qc7V;W^@yp6G$Ij zrD(0Lj}{>fUuaV@6Xp*Tdc?SlG+ghkjKv6^hc!!9Yahv&R%(zvW{jH__|j4PUiN4= zkcK{NFT}KnX!+Su^8j8Rz%-aWhFVY;aF}}qh>wu6 zx+OdeugKt8#d(PMz`h!y?s@yLDr5SmbnWW48qUoBQ{3$ImA$%b_6HmiUF9g_Twf6{ zT*8JEAwPw{?+2%W`smxq&|P1jz?NO{F(Cmnz>AK2A#xi)CE)1{Jv^H_u5(zEC$RYs zOEAcP-)rWyN9v{{022LUa|Bs>gKWM{p*)|yR1ra9z|DfG0{*J_dx`9_;dt2uI8Q*E zALq0~+%|!)XtJ3m;m)yocsna3(!O(nXE*ZPh)@B&XzoEdB8*$%77W+YkQ;{;c)JQB zO!4lYT2{QcbaRbY>BZ;|uaVQG9IfQ~A_J&@+|ZZgNN;@COU_NX8M&W# zh8gYPjT@XfZA5xsSx{E(f=UaODwvo=;Dr@mFgx)CT}-RaB_N_11?pvw01PUpCUqTi z=O%tXE;3r5^ytF-WQhkPhht2Icn<+?m>gJ1`ku6L4mhb$*c6H!{Pe8BJhe6Ni`hMY zwp{y6Y^)Txr2zt~;nVkDQa4e{L12}ugd@$i%A~Yl=5z{Pk5ScNw&1x_ z%R=`9xegpTGsstvh5Zt1eJK;imxO-E5YuylxW@0W=GCCWqazT@y*~<$E7fKwaFcMQ zxQOVQDu-R(ntyt*ge$cO?p22io@fq#yN=%c_>ha(C?)XHKWRKLXosuN{?9bw5Isf^ z8{y$d-xxnM*%}sAwiw@@cygr7tr)TXs&0!mS`A14ii6l(vU+9!{H>wC)p zVpC4_XEzPJxw!!vG9fWl;vJ(9!C0y|8siT6nHpyu$`dx!VWgXL#cIYA6 z=WTG_BF&?{EOmn=cULkmD^*s>dr75-O{&4dSee|djN88c^?cSf-$jP=xz zPzZRxpAAiB_Kdt~RdJR&ETDaVt72fG(MO|NDKLvF+Cipk@FW?kq+m2Wf0Qk7T#pO6 z3jKQ($vBPq1>GT_>VoheEL#aP&8J8Zv%Rl{5&{jPj?*zG;zQ}19&Tcx)LyK0m=v9 z8vvvK$KHQs25Fv!SDfiDJvx|5pe#Lc0L3F)z#sxVM#$pW#>D2RgJ5X%QbPZ>IPlOL zTq87#!Fvi*EZH(tqZCjg5BTN`CZ?4&I0VMogRWRmY9k8}YGEOv?^ScTfB^eubo_hL z6?2f}T=DkzICq-?1;%ZEjYIwXY<#|-gG4ESbt>49dk&@$cOpUw7+V-M`jzQiLF6d2 zfY`u(-gkW9L4<@8j$(zOkYLz^%{Ot>Ku^fMRTj6i&zXdNO(RD5&1{{!GfY7Xb~Lk$ zU;U#SDHNK=qM%cg*7H@B`Xt)_Z@EFize!AhHcikwFa_Yo=_&Aklb-@6)z$^UblT7< zx5q488#lqt&@G0g$ z^vv~XWSO7$oskIiM(q@1cS*mhku%cizH5q9+Cq^q205kQI(b&oY*lLXScNOCZ0*?C z+n|hLW=42GjrI3`OkQ#AO`!h9Uhnb`G6Nd{XH7m(<|Q+i*?rj@L~CbQ{2c)B9RKU^ccPsaeEl9e)W;6}@ln|__Y zBG?}9SuK&&O*I%@MJ8qHYG`deVCBat@3+`^ZyOZG=W33BZ|k>*PpLJ^dj={nL$XuQ z#D-HTD?mc2u&5R%T7cqmQi&jwqw(Nm4cWBMn{ccbVQ%#~R8v#?qzJJJz@O*~=y@3b zNHf^IT(WxeooD6!7mDA7O4jjFP)&?E-Oc0!W^cs?CAo{Fn83es`DCgP8r^Vu+F)4N zRS}0ZQ#smy0ONFo+`yo>3ZOEY@n2<>1L;K=4yM7L>YQ_gBX!sKSpH#$c-)mCQ2W?`R3Y1=Sb2xh@7 zgOi`0rf#rWdiN2j&4K76Yo_*x9>~IJ`fA&x`j;-Yzm}%1#ggg&8QUCAy*6=^~ z1I*EXR}{hsNBA!BIn@vP-m}|Q{fi-rB_D@T;+bMggM=dezl{99<;@*I^*>w&nu?rx zk!1h{E{r(y)D8VDEd^%HRQuRcr2%D#I4-|oRCkc?I(wN38}PU&c1_TAR4_&hsP!*B z7+sf_ep1A!gZ*}|w!f99Cj^)Q{3mC66^iSBk>%-u^L@;e>7BWJ=>-gF1pb`Fa|j|3 zccIb&CjMQkuco(@fv(MKVpK>;%9N5+;-}+40003f5Jw0*2;2y*ju2&16Hdcn7krJE zFg2dnK7UOs6pehsW8rV%;vSEDo>oW4#pTm}Katj#T3a8QYE^ zGFtHpg*DP=j^+3a-;NV;1{C_?oBEgw4Bc_+B7*Sh4BUNM`lq8&gqN*tV zioLRj)g07*w&T41a)niMK-E>1XU-=aq%vbOuS$h^4u+O#x$!9pSJljenzG5|5NQp0 z4oWY@Ii8Bj6%sQ;)>Kc_f#Z(BPjUpQ@5@WAWKjtH!5lPc^=BNK7KWbHTue{HB+-dxjPS000{7Lp!j4R2AcH>=wSo zriKor!XyGlO8L3L<6;ip$ zkg}%Jq-&6Jp&{Oz%B(Yem`?Lo08l`$zfy^!hnyy)2U$_Zgxmo~Uhx{qPg{{(#7f9# zN}-(v0RTV+GX=?bVG&aMNH*t(sLuJ`2 zE;L?=8y|NXXHo7LkLIPUYQ!W#kxGpuY+YWm2{m^Fjc7$A!?NsG!qh0Fp$LT(R~4R& zCDvfgQMLbf=ttCrc=UM{FHg`Ls?TC|r$*D_4u)Hp95SEQW}K*DR6`Evhhsd^OlVJ& zhzeOp+6*T&f5UVPiHn$+wG{grwX&hC#<^`H04_k^HgAm!0Mxsq+ zg%U5N%PdL|Q^-*-e|t&-6>C))B32!yl=HX!k!ohTmbIgl&B-oure&9$e^>AroVYhLa6FCEKyt;Z zvSk$e?l_(8pgstBdc0lZ>sxMH?g>sj$d`~q;muLQg=F&W;Z)7!CUZk!NCo+;4Ge}y zcL?Lt-V-`_@K41=x%BZ}^M(o5T`XEH_i${XN|;cDQs0wRkf6;*?6mj}FT#Qc9nv0R zSfgES?Uhvue`(iA3@tqpWLL=qDJ4iWcXtO4X_@qn7zq`2HNnARt9Hk*^01NmmQ7Ob zinwD&LdVBznou(0d%-}#gWgo5FhW!9AVcDUbqe|;S-4x8xM{Wc6ZEQR$`29r9mNqU z`j%kCKF`QZMf>-s3ji1EiqBv=Ktcge}sJo)h14-Fs(HdjHxKl5=pWOJf7$cKXHG$&u18q2&`5y~}%aqQg}sAgWj<|~R&Gq&n{^~2uc ze1;tAGd~sQuHiy`h|_t9JoMh2Q4=I~P$I>dv+h8#8O~H}reZV8aQ<1)DD&taLN+}` z92yI0a7^YUb_m&gdB_$@RxDGHS;aguZ%lfBf7Ou7=_|6V1oQnwKD!zY#G?B81NOv%~m=EP#PA;v6;o28NA&$2gX_Q1&ea9#|%|qTL*G^U*brlq^$#rswvb1%Sv7tnh**O9#T}{e~He) zg5nV>P6cy%K=L38jYTBGrQ>njRPZv6AP#HlBZ{!`h`h*@Oo*TGQX+wb_@ea0nAkS9 z1ZB09R6pg3Rq-TW*!x4FmKqvXXc+9p!kWHBev#rIlsfC5AVaF55?cpvtXy$sj0kc3 z&?-h1%KQ--eq!uAYzk$aP>c2se!!MMyHe_rn{QigR_ zr5jYzo5Gz)Q$ly;p#Meat6VUj`Y0OCt0YA(BgmBscG*v}*6HhOKKBkDMx7E6#yPf92&-gEbtt#|vEWT2ChFES@UQxjH1wikUsj!VrfXCHZ1m zk{MpHVnR(Cb8MDGwp8QPIDMN3s|cD1M?&&>iY=kp^qY!Jg!MqP_EnWY+btzW&h@68 zY9(62%*3;asZUi5g_TIvB$m@`SfL||MS*6ke6p5Hw{mVi?Nzw7e^?ut6mRaxWZak) ziO#E)4YN`3jJ|EH&Guz3>FnTg*(P>|%N!FfE1?jFhaAw{G0uf*&Kd)mQgb`(&#Qi7(VV0n?h@(TGYs^OKeSS~Az}v?s(I$B z?ImojvA_zC=X2*pe-vW6t9t1Vkvh-Pkxj{E9;*n_oX7>ahw4zVek_|E&}vcQSBVuu=A2w=nq)0Kd%`Dm&l|vXE8S27E{?YcCeyZ%^eh05&I0Co_Rs?3G+=TF{*>_ z-Pg6nekF?ssZG(TR5QV?#lHz1$bRbHi&@`<_Hu6z%Cw@re`sZD>29WKBciD93zCG= zd@&;uYVFxZ*q9`|zy&cUaxF&FsgW!)e|Z1SK0k^NDfuyW5rt*qDTF~m3K+&uftIi=FCW0XwkS`p?v5i z;Xu^WZpq?Il&%)tcfL~XDXVAHl!?*mqvj^%e+Y*n1-blO7KmhhHAd8z#A8ZizHH*j z?-W!LA`E)b>J((n80m-?b7B1P;)uND^?Q;>66 ze;-d%4^s1p`3}f+Gey27*b7l&6?Bt4C=qe56Ib+&N(b_A{8;Qq@PM(H#qJHI%F(Q# z-NOn6g%E9y`N%M*%DOL7Mq$3>4UL(bh7Kx6;u1w8BM*cfuCk3N6OU&OOR=aWB4W%U z)u2se2bX0T=OyGVMR{bAnk3D1kuef%f7!tjqSIy%`4RTC)VN-f^zvYkZ*F%|e2g?Q z9+au{gw*URy*xPwBjX@@|H`GPo`wIlsAUCw63i$3s?3pA=P*mJeC1Gk4}Y^MIRPNTq982qu^k?d8>qiVX1Oy z9vY`EmS}iKQ@m08&p2Z%B1u*RqDj>8p^ZdK52uwCHy$5{sg;A`t}*6C4W^Y0XNRG6 z%8Zf1Zipr1uCbd|Fe+%I)4(e!ecga|pE+Ho8s(^27zwGjf2aawmvXR=x~CYa zRK)yt7(*dnj+fC>U z3Zdv~3C@hnSd7s)?IH6S8G0~PNzkjbmWoJ%sxDJhsr(3rNKmUn96_vdUyZnDghEIJ z4;&JIKUqz2{?k;Fe={!;TO}bUXQaA_Efir=6?#7pxVWa#mX{k_=(DvI)tQ#@gkv+ zC;SJk8FC|cTz*ZS>4GZFA+?%|G_4p!qs3`tQEHw@(duN!45~SZ|G_^cy7P>637?*- zkYZIvv^!mcf3_P{Usi#{sk4MNA@T#6a3YNi=Y!XBhxyPL7*Vfm9J&1k34=TG8C*kB zAI(Iz2vL$&S!B-0=Zvhe$m40%XG-<(Kih~l-|Y%I)L7kFe-uWz&mcc2SduQ2xc$+T z4jK2y^W)?FO9zWS65mYkC}fpKNY|2+lbSkBA~@vbe}hpCPo=~7fD>~~h*NjVFoX|^ z`17tKxDFUZF$+^2$j)$(I+)Gye+l27$Uy}~tJ^7d&k=|YMCg)F>7YVUa(h^gipy2z zw8|%oP&%NfsoW1SSyV+MB>x1`fyu+51B!91O%_d*9gLYdlt1>UY68*0MCa|GiXr9! zv0_+jf3OcKN?fL(31c4A#SlK=NTLg^hts0^iq>2}Te!qJ7!g&R!!^hp2zeDbD2n}4 zBzMCJxkQvEh?5|4;JOVi3i7|{987&iD&}BhuOM=WY?vXdD3A>{6a|j_AabzML{2t2 zWZHBvjc?G0G&l`yL(~CP zoFZk{NO<&seBfDwT9FV%>jB9$tG>uH#UYY7(+L_?LWH#kj36VWnm#_EWJ2|1YV5+X z$(IJ|!AM>v{~ezsJ4v!YE4L=mDHHWzJ}Z+i5ATVBrQ|Y%LR=qWF*q7rv~q-3GoLzv ze>)i0CPDVFhA4Z;V`Ut+s>vnfMwY}QD6T_(z;cO3YN!-LlCq<9s<4PAe!^6=5jhs_ z2~{$UL|7X=5Gqz9f(REHHhw4yDJf%kKo--GM^_*k8v7teL+zl*6+t$o4Y30k+i-|B z42_T%m+(bjCFG70inw@;M>t|-f@kv|f2UZ;72$FTnGJ3Ud1@1i4W0weH?)I^-O$dp zxFH9`11ffGqRh9bizGuQl10p2Q}3QuLQIX&O<^H11=B%!MxD1^lmBuKGPSQODn)l2S4 zY3d1VO@yd_V28+s=X|DaiV3p%mT^rFxEdi^_<=Vv5$|{K;PiL>4LfMfxJnLw)3<~F zLZXK8=cW`+6?QNs!Cy*(%z+sF zpSm2e($HndIH6~?j&30@ON)_IS*lc3W^yX~3NqEirpk2?JGQf;5j!JIf0b)Wr^>9m zhE60@aYjp)%?gK1tg0R0K*&WIU1k!Yf+ga6!3}k*sLmFzx{NDiT?nn{Lo-6G6`dtB zmy=2O%SLL7tACs7tZ4XdW*>$>5Laaynv@&)c+nw~Fyuq2qLp6q2vv2RAr*#;&+doh zK%zPc!-+_xx^?E!);(v+e_zWh6?021b|amVD@?FBi{lG21dT+h?x2|k`QJSo(SsU8FRvy;&e|(#j1h`#nBt%hMJ6JRR`*Jc+P^`H zr&V`CG$d1vXQb+%6!q))cNw~{T}V*S=qp7asTd~VqNbvVWG>$ke?CH~|PYsK5dYJdgnlj35IIAOHgpoL~YLd|-nNMv%b> zFaQAt5#R_ufWZYJzyJakXpq4Hq8K#50uf-40SGpzzyb|2IDiQ@h(HH8kO2k@kP?9h zHmJaY5qy9F1UP5~Ot3)+6tKYvNKk+dj6ee!Sik}dJdnWze>f+Jg!(MN0uV@GgAtIR z0JDg|5o`bh2Yg_I5G25Y4`g5g2sW6&flWeka%SR4BApm#&O9nc)-RmnMLLieLcxLM zGF$4PWL-Bw#c0hM|3ppDdqrn8uK_F2;DQoVzzakLb&XW;10l<_G74rwVzyc+#dk2K zK&ON!S0-rvf4<#p7*5ptSbHaF4ZZWA(Z5zMT&{LnIMhf)q?Crx$>R8^F}q0Q**He5 ziV|@}v^s@}RZVD?j4U$JuA@oYDbWL(lLnN>_$#czAOwI{+(n&0; z2#ssyJ<&4`36Z9f)=pJwilKHwRz#<;l%`eh#6DW-(8QJLKebk)Z`4b)%dn_xqbBWF zBXexDf2W<)sJT=}t@zm&Nv5?NZpkU{7bTZlu@K|lR6#*wcg6)tC8{P4FM-4|MMxlb zYKbLN--DLyps07Aust_TlnU*E3~{#?3nh*8Mnii*rSGP9hZ>RLQ+qljEoOVCm=fM8 zoA`!Gkfl=3_g)SxGOs4~P=BQuNQO5``GiH(08WNB&@3>6kF9WvaMjNrc=`rI2UnEU3A34^niv+hwD*Q5GoI< ze<9Xj6bVC6st)17%81u~Ka`mc-Jp_+tssduC00wb66zGu0m)Ej%KgYQt<6wbxG7g~ zp%Y36v#DNabdF9?DD)AU3Tg0Fls|F+5|HFMaJb)Rrw8T0LYD$PZYc`=Av$5R%)4G3 zjyXLjzGy)J0C0g2t0Ggp98@Et&+>U-f1*G-*tB}HL?d_oWYmW))4?M5f`~>l9No~kcR`@zk zXgZXaOh~LDlQXqiIw3-7rKYJ232}zXx%hL-9;%RB)C2eBvcy_UWcm$yD^p~a6wr)O zLn5*grjCK{0Afnj$uO% z93t|Wnzsf&0TTg#MyRAim3}{$*A5;e7KX#Qh4cHHc-}B$vEOGH?zgw|;CD6RtqF2F z2G#S@=k#Lt6A9gNq6q#HLpO@;B=!HE$(#z}h@f3Yr$dlnIT2oFAP`q-3^ zER1Y&$3{|@S-6kf5hQ&?Mq=MqsS%FcF-Dc^@)`Pubkk>lRJL1mJm)660!5=KN<=-VS|Gp%aH>=e-x!Tt=>QLqtCZaeH%*mCQB&T3Z|b7#)07?ajx4+fQ(lC3;uE7` zoOUio2NLf@dC~)xLkFz&K@)O(j6_5j65C`lIw;Y0Kq4aYSQX5J5fPc0nuAavfP>L zwz!#HyPb=aAfd-omORL9NhEC$Pvs%$`3iYrA%3N0C4wnIs>n)D^%RpTl?#Pz)|FmJ zq3WEcCv}DT^0dh@5p}f(9FmZxNupDm`%6B5Z<-C6ES1CqsD-G+P)F9frstiinMs2L zV-W~INE{A^L<5r9U`q1?6aWUch&VtJgu#F?NNjo>a#|4p0000000IC2001tFW!I+N zZB8=u6rFaki-M23f>U}HnmZnvsS=!akl47}8Bod;7=KJTO8ZwNw9{AR^;f-oDcZGv zAa1hrCRJU}(d}9-0!+~my6*&!*%+=K*uLEyx%c1q?TiDvWc(#u>yRVH)uH>EDv}c5 z0g%SZ)&b9HU<8OnRDH_x2h=8mcKvm+q5_SVU)J&k%*AsAxO`!>&y3xIT)~u~?IjzbFk^DO{co^bj0>QF6V}l6U2i8wqLHYDVa`S0&}f}5Rc3&E8%wYf z!&?Cn>?hIJc6><76*^~FQ(FEB2MK~w_eVPXKW}*_IXehxM&d^Q7q*+llVK}8sYI%=!`$J7U8s>Hw}4{&EC)+cbuCz~ z>Q}oJ!_D=fE)X<5cJ>A+qamCRyhqKNYQd;bLmVMI=QACFlr7jF`(bTy^DZP+wGsS23t(O=bV1)L^%%M^ z7YnXo+%RujO~~9lv3#I^(?0(B^2>d-eYcdXo}VJfJUSuBW#u*XY9%x|I>YW0S+||F z2Z2CF6x(yV<@%!+Kg2y(*35xokB&%OBvPy?LaJVf5~REdSU{xwuS=U6XTTR>oNwAT zAcvHTU}}ko@+p|+xOYMKkfG1mjNxz_fyer)Lk}|40 z{BR@fUh=_%=4lfRt6He0unjQlD(6hebLv{)nO37-5Deqo_2efwo*hJlixOg#YdzE` zRG7L2;Z~|^i||j}+%W>qD!dnSSL(>2%31^v4tdCW%wHFYmup|tEm&3yg@5VqMWJxEBR1Rz##3W3+3LwLQH{?gzHa#`RIBP4S)4bgJdCCD^Ff+ zuUMs|1$}TUsnA4%18es4wQrdT|GDgV8FFthWV=k9yA{{|_1h-=Z?2%S{lXGC;ohdyqWABSMnb7!AZNMn26(BgV!<6yNNhg0`=`C&%}#kf$!dg6l#n2prTH0W-BEDK7XhbOaji#pUqoW?WD0;H<} zG?YSUNZGuF5yx<&QWZm1SO5DEVw+0Pgl*59(R7L2APJsNH9p15sWuWSJ51m+jn(m18I+v8iy>DVP}2Hj1;yDb0#L9 zSa;ul0fxbS{oJcG(G1C68Um6}K~RhpqPq@pO6v9bL%W$|y{J(M!5UP__EGz$mLc>7 zkgb_=cTvXtu7e=OT(KzxIoI-a9<6FOVCtKFR|pkY-3E`-ChZ%dnWztRF*tssrp*7mE zNHKrJk0D(=?D#Cs^4m^2##5Nu5MBOrE5isb;lUx429^73s_q>K32dO8LOkN%-^SU0 z`cBi4f}hCq5MvUw3xaTmgmofktJ`3;$Nis0Y5tr6&hGGQ1-~*+z~NvbV$T=O^C>QQ z-(Qq-f{Ijz8fFGIEr!}h6);?KBaW(MwkpL_j|VnI&c%fYDKXCRrLTIz z0+W*Z4AgiHWe>BUs%TI{94+Qy&QttO~(8VURt7;Y9vE;^BN?T>;CU0a+CIfn1) zFkS+7`I~c$Xia>&<}UG`F*QHEWVwDRq}dORU8ZZZ%eLmSPpT9731XC)xAIPZeKZwV z1Vr;^6DnC@F>0ql{x!*=n*~6%rDk-7z(n zPWtfkkl~Gn7NKQ~6gG%p4kR;wRsautVHCOyjrk7M@^fA%79G4kAH!*6=un=^Lc2BT zyOCtZ&&sv`u&K6*Hr1j1)k!_e$PcX_TqG8oq<*?f{UORZvr9T#P83w96*_oJ#)yQ2 zUSvG9k0iI~JfWn%P(^Fg$wVN@e`Sxz-Qq%&c;=&~BA5U=Dfd_S-PZ(v!Z#g&`+T1K z6XHZCr||)St_cAj;zc5(&~>Dk4k~V+LUL`YXNWz}N7P{J=%5wP5fR@~pPzoSQP+3) zQj7;c?WR7BpsE`dBJrYVFMvKIalH`x@d)5VlLI=Ml^k9wX0Qv=g=c~ryXjj(jJT7) zD#O|iw=U8`R^bceJVg|L?=PZghhsUp-Zu^%SaJ2iq(RhhSwZJt)Pnx~4E3<&$@vi{ zY0d89Bf9|6;*tzMFHl+@V&}Zay476CZqRbh54$PrnDsIUD;K_X@+S**;|BUjpnDa% z;40@h{DV?gLGvYeBDx)40*jf$gmmR*bf-Qq$6#0h*+>RcL;}lyy6Ktxq=hEwW8g26 zbL%BxH{xeCtP*GJHN&^qQ8SxvGkESLval?_#iP>&;h?!ullbLu>F<@M(*LAd(5~^Oq>`s98 zF}pr}!JeeKY-SaIp#$-nT?5^oDEB)FT9IHFQyB?v^{%v4ax)Ns5gO?1Y>1h+y<5r0 zl8pybm*+~!<%u{sTa@ctZpbyY_GB?-UkJ~IL>Y_TrkMps2g6(hX-LJ|jp8l{;k=j| zEyo~0pM^XtpRy5EcAG(Mf+v?^u3f8S_bKhH7pEfCv0h7md~`m4UYZeqP_V8eFr_|I7wvxd&?E-b~lO(F4Wos0WCr24B@ zG9tTSG3uup;?;2H9wBHH&g!%|rtU~s&|*pw3gm(#65(+hpc9%d<^QfVuPHWbS5Lnh zJ4cahDv1(M@Zn2|5WGTcQoV-4YafYt9B7t*F9i?2h1XbIP)qCUcf|> zxT3zUuA?;JUhGdf*6;4%hP~UvFBwr`ZGzBIGv!v~HL%n#1ao)01OtfXu6;m6LH;R) zs{n-@p1^O6W88ybcl0FwUbnLpg-}^UpD96D5I(#W5t#zCcp&oWd?Z*?<&*`8wY`kxXBDH6#ExRQK1|3>u>>iZYC&}%YzEh3BgVX*a`W$`YuQWN{vAFr2i_;hi5#Y- z^G5k)&lgx3(GKu7soGgqA4K)RKS&e;${~m9UPbqmmawCUgyDi78fWMjAGNK?775e# zS~7yj3jbK7T(b+GoIw-03};-j6J-8>6rNBxwSvEfFG*texmZagv$Do6v*BS_3JDvO z6;VD>lPAy)ISAnuQZ^>fNLht9CUy4UK!ZQVGHa>qkS!UO{By%n9*ReN+O$ZbFB#jJ zylkR{v2N#CPKxNa|JX1tw-h_J$mf?>LU3ey8y z+g?2lJSBQk)sAz}?N*X>HDf1`mw8&-i>Eqrqip?{Y3z)EW@MH!n6tG7Q|x`F1_`qg z3il;M6lgJV6{rZH{bDzB(ijdSn80O&K~NW6PhtC(>eeLkspHKFQmPn~C(zp_l!)7o zP@x48z_T2@Jv4KFT|;p*eM>6!=BQ=vD+&R`$5N%|%FI@SY}%lwCcsX8fO=H-ZG?fC zSzN}GGrz<@TRPos!MvdsP{4~C_p?fe!sr7bzdAo$$ixlqxxXk9%yfQqKu@`SPngDn zyeqANOp#!p!j*nNp&?7A_8d1yVp%ft=JIh}L|93Iq& z-62j6&9h)lFNQgQRmf+vDx*ri7J|}=yB$PM_Hg9M*;$uKdp|J8z$O_Q^f4O^?OsB) zgvZ!h6y!Mo_D~yd2+LF-Y}*kwd9N+&iS|uCCziyD#!t&Xyfk-{fA)-+pmUYBr&f~O zCvlL~c&uK3-xY|2Q`%&&9R3*)t|S7sBmH(YxO5aM64H{`DqU`%_;ASgT(b%>mb8za z3vJkgHjczp`tGKi4w>qb?HoZaM9`^Gk$AgXj4{gr&Cop`4_5TT0%8T*_4o?+PZ-N} z#{-=420NS%mn@%roQgQ>@kK!qvVGIG0-vi!#KOsc!q4F?Lh8-4(&q`Iy;gN7R^hru zImJB$Jp-c?b4NKe8cj*-Q^4yKpQ#D9gfO1Jx7C2e27YBaK0mqFjv>7`?4vq#AJ@1D z^+X_TGF%vbsnx|sDHyyo+A>Y<9ypkXC@xT9z=g{Uk?y>ZC$BDphnDjFLjT}3wdisujOTJx4MlmK z4d@0y?D;`qnVId(3&QrVandyA`{c<#$k{r7Z4$=Xp|-CyMsgV~vjz^1SemQ|u*5M+ z&-;eZQ^V|^2G&~$CTVOL2U;a4gZq=P#3(^zYf* z8a>BQPT~ovq>%hnXsN%r9?Y^NWwy|y9P|<@lL6|qCrg%p zjZH6p1ff8j;3OnpzA;$1=qYP7AiY!82z&m^t_$6uG~rE^N{Xe%QO?^N{eq6c*}^?- z<^r&mwi%G-{^B1%IS6a?(l!a6#zTzyW(0?<2%j6%yFOh3W+1MFdnW zNJI|EHz1UE!MHgsIC<7%AcW>j)4f4O@7dR=>ofDfxg*?=UPM*m9C)?P31u1;wpi6k z9q@_@&||)ifd>ZLFq>WI9MM7}23Ar`+o6cSk4K;dqY(h6*!M>cI3|QBC9SW2bi{^~ zLI?-RS2TzuuJY$CW~EN;VQySYh$_m5+MLlC!j^*6k1qnavJ7kOMz6L8pxBq-Lx(2K z-38&pkUh-1yV0R)D|60KBZ0elKnjB`_eR9ZD*ZL01Tz638%>-|mo!3dnPo>%^z?nH z$%nZ1_yk?6o5uIdTImV+u;l}P8+?eOKxbwBFD%{%kZEfDyFh8Hyo$F%Ib#;B`}XAb z+t0u)SwM&@qaKACJf7Z%a4BI=NXjFgV(&tF(omJ8vI7jBQ4BI+vFhn%f(4Uls7vASIVuf?T!BQ;m4L30FS-%el)L4*@O13@LMeVR=(BEO5XS&$WfA}<2;c7SgZ z1H$(-(ozgXT9Kqsjuld>2^T4tm`D3V0wUHp zRoNV;2!e_Ssu3>{)V$xed(!L&4U8f?H)GHD^hHl<*}hYvgYXED{GT*1VgT5MQ8~$$ zuu-g>tj6B~pxN1|VBOZA!5$CB0H7^Hv#@$= z5XhsdA|t7{i9nT0@i!Sx3C<8pNEAM`BfBnLBQv<`a>P-m4~}5(9s(Zb^ulZo0RJpm zQ_%{@9wTr_&g!LpM|6FRqr);ts7{)4uvgKJtu6`}UJ4x`nvNkp`e1(_u=^oMvvmQ| z6$!hwcW6g{8j0sML@`?X7W|h55ZSwRCo$tI0JOYlE~+#;^^<&ZZg-ihl(zU z0z&(%6V$114V!`=FfyY(nF2_{V(B%51j>GThO>X)EnuU>I~OUANg3jLU0r(t`*+6dGPcbjiu=jz=7^W|l zTkey-tF^Vbh*D)v(StZ?Uv|}i{C36pnMAZqj}zg5ZgQP%Yz%uZ-vsIFG_1546RiEhmHZwF{9TYeK)<2sM z^!Wh`A*6yqq3#D@rBL=E@i?vT=2|O%!ln#~^eb6E=2Tz`waEOq?g;h(Mj6@ZrD&oxG}~HmXQW z1KViQWE>s2e}PR-8Dr}lBEr~g;(7%&#jdB=^bG!olMG)~C^bt1RYah6cX2I$b_#}? zm}#Qy%-hIPA=i3t$~m7G=`v@I;dg(*0!fuI3h*9JQ+7u%!&k zU<-fr+X@ZWBwL5GBj`_e&Oh0LaqapVg9>J+TItUVg&vAFpf(HiPaWnaD;_y3naR3# zAeI)(U+V_GE?_^qTuLf+xYx*k&xf5R#2kbgyVGfPRM@nE_&e1B(iCn`#- zsnG0_Tv7k`d&2>K)}ID~pnQ5byVkAn2lu|d;bj>Rq)r6Z+u8`F5C9AI5@<3DySWiAE=)SquNjKVF`E^S#*#5ic|kNL zPYtuXtji-|WcYpiqi-aC3f2YsHm6x$Y$-EPc{i~Q1?#)!+h71s) zjSa(n(J~gseM&YDLU^`Eh{Clu5KCBjjA6G>L3P*LqhDq1R8b>IaoP-rrH6Eh3OaM~ z?Rm(Dd^!EIdtp;fC)XYkI&^< z0tq%Y-hBL*FSIN2Mi|h6>w*$8V`8A8(XbT{%SR*yU_uGIquV=Fy_hFi!xWKrzzdl$ zN<9f~Js!z_5FCrG?SMWC+pWCNE31Qt(ceYk;Xe`V3YaHB0^nZ(aJ)dm{9U34By?UQ z#h3LNXhS8b+bTPMhv_%4>1wBAD=PcPq;j&z94iQLSYo1G)kQOOI&?kqn`=C{@o!tF zk|QJDkuMy=b}^}ygtu4#@kZOX2~vQ59tnx?b{TZ-;uW(ufE@6}0W6b#5rq3mLR~Ch zasMYVu_084YYi#QR%U=Mk!dUBXfJnFucdE$Mx-Ec00t?4UXf|=7nGuojjU>W0S*XR zqtME?AdU8E%i3G?IUFZTGKIffb?@Nc`G3PdaSnL_)|V7vA@DA6CuTbOSLg}Cu9f5( z4qZ&Lni6s+F>&j)fLV{4pOIlcM`Kn@1t)0Y*ll^Nde<%>__6B!J6-1`7XGB`$l`;G zB`gn>_}&eFDnP^pl`|i3XXCpA4#>AlSSfVa0a{9bQAa_`2bNH^qy) z)VIEchXwf6`8G>fD0P!;gL}}AxJojEqB6)+J z-mMvb(Q`*XFHSv?t4E9wE4On`|XgT`}gev*%n6re7BhXVG3*C3495IyfL zDO+=tr0qV8?DC&QfpP7?dRPqzD5g>eucW7u12ms@`A$O!kb-T!%agC!Dw`1_nEP1DN>&A3<_9g{jC=gBxjdW^Qi zKl1$a?m;_{)o927tPfLB3CJN4nZOD;fQ|4gFt<}{WKf8HM-D58e2$7A@c}n`ux@W_ z#4?sF49tyS6FNnY02YTQmnOg^?_%>vNb&k&ZKchu&$oyf%o1!UL|Rk7zM*Y52e~MJ z@7FGe-BU2;d|8*dTfd#m?{-uRD5qHcunSh!5c6Vq_27bnX9dgJmX^%sd%$%w05^~( z$fz@r8@3_S=CWtp;xwJ!@MqwVV^qW0YB2de-XN1^a?`v5NLn4iOI1WAz-|F$fvHfj zB~67&Un6;PiH`)uA0~_s?=XoXOj!|s;sThQOq>VWiuyy?s=~w{)ljFniaz)@| zj;ZldT_&{t6d<;IBJP6@9e)kg9BHDJ$7BME@4e8BZ2cbnuD^}h)GFkz>7M6*L3t7q z_$4+Yl}W#lA*NP&S*RI`vZa-D{+e*@s`;SvQ4nN!Cyn&TZm@#actA#z%KM=cj z35Ko0F)Cyw!@#vw%{a;^GDX0BZ2);w+=fVc=t6X?Yw$H9;DOe|`c7y?H7|`!KE3xB zpTKSY^!QKs;!~p(D`J$oJGo=Fq2DkF&+t_x!y*3meh~bbjAn{lGzXA>;7~}QP;<5L zwRPWZKSGe2=xk(1%)GTp2Ca>kz>GBcGwrhzWpD!G*RDbJf>21}rp5r6$O0HGhzy6; zu&r_d47^K0#vi?yPYDvYd7MeGLYYpIb`9~eK<$9!GkN>C)8Z9DA2Hd$PvnR@8|UwQ z-jEhnlmJ&isK4UwAj+L&MvEN|eqeRHq7>4Mq~`q>;ULiDO`7fX)xq^?IMbeK#Da-^!Tam%r#U z=a5+KYS6WgjDbW<{Wm?TTG9?>JBs$18&r_C;JZniOPAG_Y**eQf8K>&oDGhYnNk~p z23UaH4EwdQMaAeyd0%$LE6)h0o0&XwTF=DAJdBcxSIdhL>_bN=4epMmnMPKHr^!pC zrj|~A4jXU=jPEQyg3pd?a9FbMc94f@hr#FY71L~}_~DID7X{Unjq`F7vd~D_%V2iP zHzMa(5S`qthw^7bfAG<_T-imSF{ULXu{=&LZI;-#wdl~mJlt@%bpFE4tp6oq$7Upf z8+jyK-+IHjcl#GRdljPB7@;Srx!9fFcm4vGUcfX!PD*(mgP7yBrUt#WUlEp zynD5E%w>o~e}WtYdhK>KAN(8mDTXL|a#M}6D{A-TZuta@D)CNgCPB~%P0VcR1+i{u zID!8~?sg(1OG{;;X!X~fI0|mUOuu~aH@*`d0r)sQK~K2kvWI?hhBRkWF0I@3z+Q)V z=L9e5L*iudsGNntuBi?3jp%Toy}^SPf0cSpYlmzle}polP z8{}RAe+@^K*Uld`lqi~luER@lE5BS`s!Ty7sDX87eBeJo4wkC~YsDhNg%eO6E>Okc zsL$^Y1Um&Qn#SO{_ffRr+}+;+mzGC)d9QP`&Yn*L3us_VGKRq=d2#|~{Nm3Oz^P9- zsRdPE!MD{Z=tzN+INh^PU*?^Kihx_rfyW1^e_o}?bFoiO?=0~ZP@7CiZ@x?Ak$HdfSnKpr9Lj-mJyM6uJH>iFg}7`+HMXH^pSaL3n~JvAXRo7qNW@*$GRWJP46se#vy+EdKA9%f54&1 zQtyV|Gu?EfhPJS)hxGI1bnrn1!)HcPG0VM zYD(zEm)V%pcffbsJF=hzfAQ7st#1IXcL@&yGN4+v+t6;Z!X!`vA=@_?r>qK1qLw~^ zy<;#)RNHd70_cRo-{w=SMF->))RPX0^Bd8*_0fyJ?@sWdYv#WjHrGWe_>^z2rOj*h zltX*J`Hs&Wkw}B9nt9kVL1pZw6>}VJg8`~pdC&B~)G-@f5jNLnf7x7vUX&HhHH)kZ z?o?mOs2Xa)4t=5+G?EIjwsY`(cehfAD^y}O(Jf+6eP9X$RuTKjfL)F_O{vGt4p$*o zo~m5(4m3gPSeu>@Ri3NG1y%AiAR#2h`{PV5V^oC7Wl`qVs&~=LWItE4^W}FE3$!LD z1u5_0IEg#Thv%0df6FE?QBAHc?Fkpfd`)(EGB&1E8&4*?X)PZxds!7|^!55CU5T^d zZ53aO*P)|wM?HazQz9@dL_!wdV`CQ7_#AbGH?uY21n5D+3piQ6W`kfGcjAE;p?0$8rG-CQ$VcEx zGX`Ovq6Lsde?I|iXe7Xm5_~1HK_qii2aykt=dbDd;a6ZREn+ZSNwtHJgY2}c!NZZ+ zqhxVe;N3Nhubeh!R21zX+Y$*v((#6Nd^x#Y78Q+UY z2XWM3$TbkkU3&}yh%HCo;27`LA6bbt1np}1l76pN?cvsbZH}}+WY?6RNfEbuP!}D zjbXnff1#x71L0G6M_^YT}=1sLY=)pLO83T80swGqdY^2Z*N798S=}4M0C%e$lU|F-FF?RO5~b6KS^!lm$ITx& z3b=25rMxS`P@-rpq>>}$+NfMMS!eo>e-MrN9q+fE5L<|Y8M_p<%H^~$dTWHJwdhQb z8`VqY>Qp3g!>ctEQmI_csIK)j!cmn?lrCROyYt(D*g`Q@PK|}CWQ4C3gh#L+q=Gox zZ>WWepj^9i`*q}4o)}^{4G7`ERX|hKC2p<{Bb?6U>ALU-U2L~lU3n7~3AcvGAByDY` z2_wx!4)v20;5l%5k%`3hsIJbWqe%;OD7YF}2*kwUUL_AyFFPGa|iJU{chzI?=#?UGiGLt;Ym zk~3pSOS(m*vVi1z&IN(Gi0~?cFRSECyyYblR~AgH>Pu*_=7GAl8)TTPIZtjYV7;g+ zXQ{38X$e1S6RSeFY@V`afABc;LJ=r4kqdh6G_hJ}!;~BWWPwYREf#E~=@+7&_=4^k zn6$g$q{D$lNe-9`AjmX5aA0{EjJ*$%3;7qYl;nZ{h$*?6Vq-E6H@QG8lbcEkE+OI= z*^WdF8O}*R;9PoOWUY|RF5Tj*n;d%WvM#mL({D~lUp42xOhDs0e+d28Z!kf~!`r;m zC++K}%<_i%CXk$Uf(GrRp_r)^$DA*ZoPx95EY`k{@L+Z&Oy$1&jhyA{A_C-w2;;c^ ztuN3)##QqRP+D;?`4>QrII5_MQo}jhG~I;L;6)cyaB}&SYJfYyO+xw2#d=vVzR7xW zw9}h#APtJQ6YU}be}nbUQZ~7t8jABj+x8+F>r<}#ef2o^Ea7>FHkv17Q{gsF5 zGI+*5Mb6+>K)*0BPQ=FZrMwJXpUcW2Le`!u{Sw7F6>J0L^DD5#0sf1wLyTQ4W&jthyR6guP55cRcLL+NQj(H4%B)# z%F45@nj0A>e;qTkS?!65(MYlq@p7dez2&7@{#a@OL{5svoP?=R z4bW4-e;MRhm`CBYkh@J#;839ZIl)RPGa>sz4#kxKS;%oj64DAeEZPVx3OV#70&(+% zP$CZkp_`D)k`JIDR0?vK>kA0A3vw9&2b2~HPru;bLO4HZa9v7^1BSXc5=0CRdI;+6 z1wIH=y%uP7$T8UprdhTWWK-M;@@UQyL=~R4}`~i#tX#-sSI4D~H zrwvH}Z6j|0e0&_a7Jx*d5B&p3M}q+Hu>SwzO|}0mKhDYRztxY!qWS9~xybHkyBtXU ze^r$XhbXnD!`;9`O?XcO91gf;41L%BRSPy?LMR*_anFm zXMOghUhVZye4Ncye=SqaAj@C~;E9iG&7#lI;~2~Hs`R)WTOcHjWoRrj1!PF_5>}sA z$7@RZEUS1VWh=+<_Dx2Vg5SwsT{T16e~f=1OTc;$BI%6Y{e7A-INf{qcy!OeAVQ(^ zcFW+wx3zDo$9>(|Z|Q5(V@F|1s`}ThT?7EG?;?)Tu?}$ita=qZ4oRb~PZomTbO3p? zK4(HHk#il>c>$y8mgU8_m=gNN=nxlsM@>6)+7Fe#JmtO|u+OJ3#f5|QB z4_T0dtr?s6-oCkHe97x7zKq9NL<5a@KJsgD!=o5kF6{F4uTnu z%+5gqhqU0}o2WY$ykF}!vX_t z$!yP`znOKcjiCF+&!v4hnRDp+f5sgH$%t6I9R?!#+RfJy#%$vhO2Bz*oND?M(zpl8 zlTfkfZ%6SLNdR>KsJ4*^IFw?WOO`Fm7^h-s(puE>d_sZtJX?-SMLURz&HS<|)YPnyhIYqXK=XzkH1l+##Sy-#Ghf7)p8j1{YO zIvn#*?G4aGk<{h|6S`2_2rZN1Y0XHnu%`X_l4xAo05K#qC2bQO8GDI}t22;Za{A2w@~YAw*9br83_eICEnb6`+mD(k79-2c3(MUH z1XH$^;9K+gd?ow5exe}L13RTg@Q~%VY5+TMxK`=lIpA=cLdm3M`8hxjlg?KB|*z0=#S#^_GAzw-K!*#0|G$mK}jR z5JhW6pxK2gPD4PvgQ6>f0EdC1-acrc{{$KN_o-Qc=%9G_)Il7iFHcxz19II7jnIH% z(Av_5nDZF)e`HPsegO}}DI077-gpzARzPw#@ksGMWgL1jVD7Kutk3 z5fmP6d;tNiqOA%Zz%#Vbxi^3TFHvd+n!F_>UI3|C0;DQHP{Ah9S7%Gb2!JnWOFkeC z@H!h%TcR}L1x$_ExQGdW93};l8c1p%h>v&GfQl9c0O2$?b7A* z14VGk@wYH+s~NtekZAb6Ckqjsp1Q~Ohmhv>6YtOhXRps2S`4oT>I{8V^=MuQ^G$yR z3r&I1e@oR9TBPS0G(tbqJQ*K^b7=WZHfT^KFJ=pxo#QDwK@Xwg@jeg&48NckXsK7B zgFsN#@8TVxfAxOh|Jj4PALXa0a*yx*TvS{`&(DC?z7hB_NjB^g3Fq?(#BqG0jR$qg z)Bl0k*UA1p0K-1@kDQ-Isz}yD@NUQITB*Xhe?f9@siG48Y|XX3Ze|zr092f#Q$=Cf z8mR!ixm6Sd;LBUxCnsaHEZ83NXC}(R~XwB9FB6Y0=!BMRRHV$=+)LKS~)WsIq z69{{-=+dGp(n3-dO^#Y)^?BmXunGrhEi?dffGDRbkWp)pfP4%Ydt{fkLH5P5lwmigFpS-W(JO+tr%~Rt`7oI!O)QK%QKi9{ z0+9=>EcA6Ag~86x(y4lwWnmH+^FH7NrZ zS|=&3U@M6dLLP!38YetSFsB5pt=m8_MAn6vQ8@IVAoTTbrpb&g|BJZ6L++qMe<26i zg}O?>16CLDoNciz=`~X%#9l2^D~v)qVnte`S~?06J{&i9Rk@1qYu4ckEZXDEr}Z8BmGEQ*vh!Tc5{2B zImoy~GwONWM`qecfg4X7zb{RIe?q}YFw5a6s;R*`wlO%O)7dZjjQ2qM3#$j`v(4U( z#U?((K2p5bdlr+RX0A@J^pwc~Z_nri6ICSQ2Th@7NPrL)G%`65_Dlk5rp-3TgiFd8 z^-Ddo#P70kGh1xl-tNRwYqV% z(${@$8-HJV@xGpmJ;e06qIE00lT7Q*L^70yg z7e+bsfnpK;U79FT4}F0I4=1-CMBkO@Ph6rH;^~D2 zRbKT(UqlhteU-de);_r8uUTgzT=Z9!>~NhCq8c?{v7@bwC}#A7l56(3=zFvN*aCg8 z9EbY_AtGl9^dnCJe+(2n-x@!Ul-pPAR7F#G>T-OP^% zsMXEot!6&1b+T}^IMdAcLBp@wZ-(mfV?=KLHS;Hl2QW1Af62s?TX}fosxtq2VGB!{ z@5Re|b(~*hnV(M|vX3_S> zoS^Z>sRzv7n*`<#M{6)8pxGn}Ou5A=AcCH_@MUT=3Co}bDp-rAl6KG3!X%oCzci2e ze%XA{R);xqf5Fk@nH5c9j6lAuZNFRGe!>w7>Ysj@D((Yjf)jz6CKiog(`sNIv}g>& z?HA3Pq$@BREhHtk0Ki2H#nQOU$Rd5HO_Xh+yLiq*z-iE7;oUNzHf2N~iB^85R#9&Rb^t@X-bm=eY zfn@fH0zXC;2pY#&rq9E=SqKE`3L!=HlVJ20dc4qa$08CzGhi7Eue%Q-(V>>em&Ghj zgw|+y#AHy}K#xCwrrNs2duN6zM8%Fg{lC139N1Svo&XML&d@-Pifmr4rQ0T&=K=yy&+XHXz)N#onu!~wH zR)BzP0V>8W_@|gdlc+JKTa9U z|BjV(bB$|2Ih}hWMswd|{9^@f^i+z20=Cs{4mK)mQyr*Sw5QwA|#p@ljkbU|2TR?~IMm&Mod8NmtRjCxhT ze;2yL3YR-FKJwRI?*lQc2sDKl4cfk4EtQ=j0~zf-3P}cF+^^M`4x+&Cm1oAz1&0|e|ObAkNqZR_*;tN!O(Q) z+o$@^I2sLK;5Z~Q?hxl@z^GwwE0*D^e`iQL88r|JXRTn`mFASDL^9y=Ly!P5-XF}? z28p5_gsRFG^g^gyQp{>v%&GvzfaiI*l&eHDGcF5fL;-ZYAwzXJGrh`IIG|VX#MmE8 zVNO2%Pb<_<*VSo3i=h}eK#S5JRa?5L441M8`D#$s7AI48g*XbMp1q@!Jeh2me1;&q>^uwd_I$9GbYPU{1GgGZB?<19jiiIYNB5&Bn#>GGr(;YPJWw z_4tt)6P5u9Q}V8M3Nr4r+=B3Q;P`8Fm*u|)ZkAn`yg_0n56gjGDlwe)ck_aJLkWrx z=}AsP61nGxX4MZEFfsxIuyE^(f1?hVFVi{()gpxn zV6aOl{&+BxEpA~R%V`mf3J;)xEe;!e$KnoPq z5ls^f!0isz3Z~Be^CGPLvYTh1gLABh;2cm=MBp)8u;Twu>`>bK597_E_s`X2AI8cu zUwyaK=$-A=q0j2i9xj}0@kA4#PhE1kWRzpYK}!qWjvbgq z?Ol))p5)3ABafrC-$Fdhe};dfp4cw&m(lyTz6QJ1%dJc@8d*_u6BB8r%Y;o>vn)x2RAFc`!?J_R!zgiFp?Gz8aKD(XKKdD}r2OwHUXd zZHQB8(MoVllU0UaPr+J8^y0!5sdX!QRp8^w+*GTsEhL{JJ=zMfe^hMA>f5M=5LoUD zg?X;E{Hg7%X6-c5UKO{SfREa6nff->!!C_oNjX!A)kJ&Fq+$Y99g$X4d4kC!8sn_t zjEa$_0V`SuFd2vl3zoGj4b?Uw_*t?u1?4oPN&)S@BNqKofEnwPPc$nUsh&L^k&t-M ztzlQ$$sM6p1)aT=fAUMYBlmvn_!7Z64QJ9DahFrbSmARw8e?W67^T0kTP$_htVB#H9dmWC*

    i7+#Y&jLI2i5(j-1jBg^ilGP;gZ{DdK6z*lIOsDdE|?mTl6jG4 z+4QTXL7Qs7^bjN|y2~&Lt?qUr9NUPt?}L{if6$G3fLJQd#Ft3OsURdzMpW&SX5omfvWVg|5^odrTG8aM=^#Mn8if82}$bVCJxGLH*$C;D-#^aI~Sl zTTr)g`ZD6f)&iX&AN_}^n`z{D(zC1wp_i42# zcVY==RnI@6Cir0Sp;Xf`+ABE4AuFYnPMG47@-=dxzqmAw)VEQFpy|J%243Mk#cmZ zfI^K~8?wSp3nGL?p>YRaql>m`kWBqy+J&LEdoURUz#yg+oJ?I85P>t)^sxjQt(m)? z$u?Ar{^f6eaBd+0!Uco)vj$5Mx5f0i*I!xa;#w*Vb$O!-6>DmTcFL1LzC}Wle>4J= z4XdUi1_g~~7XbfS*t&fRX`o`wzi3zjz(vBgo3xS4_ANKO=ZR(x=2@aLsImg>*j^AB zo2d~^B!mfspaYhP3DO&eJ^5;agBkjFkSVF))|%;x?3CtB|9zaD2u>k$aJ90a7l}L{VtYWjmrynAxML`K%f4mx}(fb;7ctJ-cw zMpMMqZpVA30U0%wTq1p8&q?b)3O~#-eGRZdAUHsq`B9Z3&ID!hu~QA&H?+dZDf;J& z^M=7&D=>3%{1L9zH|S(Qe``OFqHo`0rxkYOSLBt;a4|G&N=D)9W@ZTjUc(c0h5efL zy03QL3F)8b&3mnFU7W4iy)QAx7br$5hUvz`ZlzM|+5`N|*EOV>`N%R!An@rxCY*;! zf=%*~b`@*0B(@k5j#PxH>SHX!j0;>vRP4$^HA|CBOZl$_$V}2^f18%-C$e9Db|m?y z_JDA8Vr*{Yy2$UqylIL!%{PSspwBgQj9fG2Fo_iiQU`YlB>+xUA(dmv4T=d=rr_Y+ zj0t)}jEY6sFFefZql_QXSbP_D^gt!9U;riYfPB%IMhI4a!5XqO=v}hzn1l`vM-4&N zOpQ$!Xq_a`48X}@f4ITLz(t^-ARv=7lvrNUWhPGdtfZo4a5@$iwyY4;UhF*!s}r1Z z@;Z$b()uqbzf+Un>sEDHdargrY98(tf+bMT{ZAJJz-2%s{9D%pq5u@8S|U%LJ+8lg zoB>K-H3&3r!ylL}Z!q_D2_z~A2fnju+9d&B#-hPb9I4gve?e)_tQ_0>uqG{<3ap}< zq{nJkRL8pv@f6h!?J2w`Pvpo;;pwT+F5=G(exQ*sngC=fIPI|`M3>8pQWhl*F_EH? zq&ipNtQ}^;0psw_4yHJ&D60B}YI9hQQytF2P{_W4>#m6er_a|jsG^#%$Kv0VV>%q| zu^$DqtQ#3&e_>><7^|==gh#~*iUleQs1QJh)fIvxZO`Rp@e*6$TY461A8Si9f@j{+ z3;b!WqjU**$n7E{(8&?=M&I~3VS)Uhur7=OK(q*V9~5LEcUEAt=6|-FU#FktI>r_2 zwp8Q}iS~hoQRLBO3M#H4vki*(092EEL}cyTgl|%Sf5#1q-zcAg0J$|$kWnw;-s`xo z;YlJ!ZN(ug06+*h{;4WkSwgC`RYDcTmLNzS2WVVZzXK*V2$gzATP| zpM6(4A3Ck{VLNQ-S!~8ECoVtszuR2{|74hapB%}iPpg!MC<;wjzY1@(w=*jQ_^n#r zgn7iYe>o$&%?6wxQis`5)W0Vh{-86Ml*-DgJj&n@GdQP2jkL1rvRSKOc6$GH9szV4 zjfqn6te^;<5FA|HQZ==xjknDzqMpv%t##1bqzLx)M;Z=5`P~|BK0MaHLaoa<&-{y9#9+%-L|v zq3n4*XH4c@5G~AwX1UZP-r(p9YSXL6Udc{d*9fT~a0&EJ~4|r4*e^{)2 z{A671(}t42uzff}NnnB=Glkfv*nJ~Yj5Z*An-r=!d7=_&>mHaYK0rvLGca7r|1ac~ zoNAO$p2gF^A^$06>$Ld5*MQExc-c}lN0WhZXxaUZw~{rWU@By^*h=u6^ZG~pOdt-; zsdw}ly-@ujX52f!M2BFfLsh)Ff8W2_H}W=_#!fzdeI%435QDt;Nkh@M^>rDSY#CTZ z*CR|P=6HvzmJ@9wh&|H!`;0SaBS781ztZu8h2w#M(gXq$9YAHoGM%e1n0^GYjS;np^ym5`i=?r3iT@88O|d>suVM_y%GC|pps%$cDT~vD{^Q=cL0}&n{+0*mhIo0}(-?Ro| zVKP_KtfwGV^nLfK;Zy*#oI!WACDG74S=#9U&gi#E{~ZcOO_Rde|n+>7{Y!35CDt^cp50a ziTlkvS8As_W08R7uT=TBB2USEeV-eZU+nq@Kb3^%EzN~-NN@;d7Hb147uoMT>I@5$E<6tq*}%|+2eB`+R^^rpf7|v|;RZOxpGoQ|iZ?+Tt987q>~W5t z(1*`NcYKT<0Wzdx$2acnx#z5=jxSdNf^Wq4LZZ+Je?~4atW)#EJcnU(LE75WN)F*Z zSBNXpucA5J#i4Z~hhnIBD!Wa^JBMFE^ybfD6p*ym8w$Mg-Bw`KE1J?l ze8U_$4A@wJ%V8{H9|H)(Uy{Qppf<+Wp_Odbi_{qA{$gr%`I%%|j-W!}PC3I#Vxet@ zbe6F{fAFxaP7#v6xU#A@c_Dl;tY%kn4kdkBF?yQ0B6uru9tIc)8hoq9OzekU>n&|lmQr`r%x4O$|!A#i5( zf2Oh_Ct80J8}_|N)Q$%BbnrXGFeR^HxPw`5!xG;7F4{0?%NkX-Q8vseYL!xZ$?3e| zmE8b>^9?iHf)a4y&27gFcfaW3=fZ8ec$0*fpimcHxC|G8w&&Ux67D*FyBDG*l5(mS zS|f+$=s4rV5hAGDW*p}TG=vaw9Xxh*fAJQg#fPn)72>w_HJ)VCk+IIVS=+XH)4JIP@(`#<+4Lz$E9?zocj#g!z^40% z;}%e?)DziuIom)&#(G>^G)1w7VDeIy0^y%{1&e`+0}G4}iTd5w2Ffr%ocsbkf43n2 zG=Z0u`Ngi}RDw4!0jT1$7A2?6O^aiRt()wY*YOVS#;HO2*ga3`vpe{w#P1hpv5j#@Y5ZW_s`Q+3z$oibI+NZE9$bU5TE zeHSV>ZZG|DVA#1XT1;0@;&>xyR9EI0;u6?VyhOpCS9#?>qN074j2{E9aDr%$WbRE+ z{;BqH!E&Day4(aAF_HFq!M*0uB}F`sf*`+Zr*79+$Dg}!W%1qSo`=PY{^UV3)DtqWLIHt9@}K8Qlgv&Epa3d?+GPwPj?F$ z-2HB07BskfD(kwlk`nj=M$jXPhrrztcwHIiU9Qx^jsp!9mVVotxp6L2a{HMP-|id< z-PZkei1mfb=65?up5)=BfAk`_GC;T)`NYkn*qYjE6McIszfG6{+F){zp{)tRw4 zIL1+-v93IXo!NShiFVS0^)3d++b#jx1%%RJp*5(5;uHX7Te%qUtV9uLbn@^rH{#n ze>P#`Un5OjVpWvqVl06;Wfi+d+Sv4-PgLicSP%k{OJVXQFATZbS91X3xsK6{ua2VF7*}V6h#ngISi&2>sJ9cEY}3D?<;mqxaBO!#a5we|y;;>4Y-F?;rzpug{44#izyXP?FPHrDI@`o$#TDsbqusu*9R&Zt`RgQ}IWE+e{dTlTjS+{l==tCLQxn9K>|~8%>)7)Z*DokEPmu=Vg!sy#m${=x~cq}OjT^x zaZ{lAjB;X9_wo5_xn|$PeNf5kulHpZ>?T(IaZEtXt^^~l;Gh~KaCH^dfSJ(YpkOTu zWRKm@sXA7%y*FrQ{m`1rZ~-44^vHqMq4~lke@%Z82#wcBCoB$#UMa9qP%2V$ADp?A zm|Pwd2&KQsF7wu~yoWyls+ofU@oXY8ghKxJA+bPcbYiRFMM{kJ8s}L&7Hq3HQ^2ci z`wHPeXd!a?nI>poAXI)v3?7t3v}iY|ZO~{P0Ul6f)D0S^2C6V=S>!t?dME7N4NBu4 ze^E~-Dm@NdPCNF@Ju;|fN-wN#E_*`0W)qhNy$HUo3#RWlVF%Deb9@Jwg7D~ruAJEA z5gZS9AenzZAKK9Hki;C?mMS*9zb;@FKHU?Qb^PnQ4Am=qL^WtS`ipj!tV7H#g?;|O zQT@y-os+g6=t^R*Ip|>h&Xgx;`YmV#e??gf4#qWmqbAJu0caps$H3V7$?<8R@tXuk z&G|#{!7p`?)bG*F^*l)2M?IKkHFlPTTwl6iY1kkn<3#^^{ST5M_-pK0O6$;ClbxG2|JIF zvFk)Kt6)G|IFVcn9eH{p;q8b=JHQI}1yP-BRKfzPc!kNPH3$nRf(@LVM;B^EwlfPW zn+hE4wVr{E;|Kv1K&m#CPvI=`HGOnwVgVXu4p0CrVh^>n8DP`s7#XHfe@VvSqZ&X# z(gO)|<`y^{Sr3`9r@9n}6c~!$Wf(H--6HEGh-Ceh(5<@wQv3;4MER=P&(R!Ve;#2r za}_b~U~v3Nlxy%dE+&rN;T?b?bd!E+i#+Nk{bbIYWH)v=ZRLq;0L9WzqSH}~vP3g^ zu}#rN*P%yVa6fM`T4d z6Lfz~C}vy@CW^y;6@YFpZA?6@E(bu=G4gbaiKa(em?aoCgpt7mf3f|9)fnW{ol}Ud z62wH9sQmeZtk-Ciy|a{41uhr|84?HA@@G=XUpze`9ivcIY5wfE-SLQlgnDHA-cHW8 z%oB?giSl1HB$!1974T2DkHI(n$bNGMfb1mN@lOflSp7v_)H;euOVOWH*`x69C-F}q zR!*m^U#M6}#cDChf2_4sx!Xv&XjiZ06VEsQ2T*>Wk${#AXB*{j)f?d;eJ=pm*i)3c zGFUPr*q9I&G2@I-N%{oZwEs16EM?@i(k5?AP7Q57smx^HW~WBKBVQ^76faSiC<5=q)V`Ob;Kb!Bd9X{cOPLvA}lKgXr z)6b^suAM%7R&e>;JeG?jXQ&!u0-O#L?o+wxdiV+pf16u1H{sKayIN$Q=?BC%q9iK| z$WW;@pRTkA-t*C~Cw%xmCB40w*nFnqm(jk9w#l`cD{dcqS(x%7gx5mRA^EPittA?uL95 zrC;%RuO$6naKu0i9PpgJ5vP7gyAWY~1es_|y-(P~k?Jd438&U(2W*PEzY9J=Bl?pT zAdOw_t2jvlcXtZ7X<$ZFUK4ya%CJnyH_=r=f9XnW^6?U7RJ&mR1Ymp1so=ABj!H^3 zg%w_ylotCM6x7{>;uDnI4wXq$WxWPI@x|pWNFSP-!h3dqNwSD0aG`@CqT_Wlw7WO|oZvMCl+PIHQ3^3A|uhl|4~2 zXlr#4gSH+_nR%{G(7ZU$}firYHe}Z=O&zo*(KAw8t<)mv%gpLr>-D;Ro@0xC< zbDS40J!>!Q8li50GxMtpc2WL4A>Q78*A|zTtSJD(`~2M-;ydYg&#m$$mv_$&Y5-$E zoWG=nJzdwv7`VD;CQQaHbWfSs;C-R0(JKD(ehuX&-Lt@of8XdxS$o>5RP*y}Hp2|| z<$u{t91yal1R1P{q-lFL6y3fq9E}2)x$H7geNnipNn!ewLB~Mhv65-;IsQG@mP|IA z_lx9)u%^eaP7j0e;g%P3yAN7n-4mQS)^3MprB#1m(=4@BZL$W1=#9crYzDR&;h=K3 z3P~A#_^JTXQ;97pZBNDBnj@CN@=hi3M1OT2OUmA*OTt1>O(XZchU>_l%(gwhGO}k8 zfY<8EX`H^cJ4}hFiJpn~PpS>5q^+%|W9IijbCpEuIp1r+0fRKllm1GrZ-;8nQcoQF z@Ivrvn@Q{0Dc`uSuJzn^NCZd%+Cl+xQtd%6ya~QRy*rKZHC0{HfyWo}6SF>{`_(8)YS2z~p!wVnc~z zF`5dEs&t^4n*2?mj`(5^<_*6-cz;giJ|+~|1D>+4=3vAcp9g+YrLU!vSM!c=N_?mE zeBF^XV@gk>Fla`eJxKzWp1#oEuvJvHfdl+X~G_r z+d3{h0}j@_LC>a}8=VoDVCfjwRS7uH9lWf&_qoXL7p}o*qw!I5Fwhi4PXV@>9Q5po zxa9;xM#@v}Xr2*zegKtNQ0OUiN;)m{4AOX(m0$$jKBX;F4?VS}T529ZnnTYlEv*NF zq3MBFRR3@*qx6f=6Fo6Ei+}fBK=gdg>r!?19~1x`7R1m@IJ)Rx5u1sg4Ul?}O;2Oa zT^6<u219L4WEuS663nmf)B-2g6;t!6oK20kiwr!qq8@seg@0H=qPc{CGA8 z=LlaZ(7}WkR=$X`Qt&(~WOb5hobjoSh@b(bize(RCapN2xI@XU(ihyF!I+0Wj5G;# ztv#e6;gr+Py~ftmKL!CrgDXC|rmPq2v_qJB=eXAB%IA#{k_DzUI!&MZn6LvgNO(4S2)cqe~a#A zji0nw`%mD0jUUQ@l&W@8y(xa_(D0`0DQB2SwP1*q)x-U8Mt`i>xJju}mA*~#fr{q% zXnrZF@6j}m;@2KM4;7nDI{e8d*`$M=HnB;GHmQG2Qjn=tJo0df+L}~4C6(S0RV9() zj;=v+sU|^kWC%Md21fqVqzIv?24>TY_@md-jK~%R^|=@~R5zM2R8X6ii^%u`L~`Fb z&;Mt(#QJMgTz^-~*pr3>9qmjA4zp3+Hq$yq!ai?18fXN&fnRQQG7}SyX<6G0u2FD( z9&Kj=sY^C<>jFPu+fEK)lL`EVk#{wee4bBaYHetrJgvvqs4L9^7YEO1akHgnOz=Uu ze$QH8^G3*=**jvqHH%UHu#4F+mZ}O{hCr-o!=%55RAdzyDq8W%bw`0W)|qlKFSaZm z2b_|k!|NF#v4y{EQ(ZgJpWOa5TdeDOZ>C z;0qQ(=#o4uMmhp>8TNl`JnWWj`?kK=_02D+yez|pza^RFR4ltCv~hKx#e$;*(s;Xqt%_ z&ThqgO4&V+Y+F+1dUgK^iK?GgOR9m*)*z(x)(ZB_FGyK8nqkV6Xj}~Y2b!+Mg;m#DID=*j{l8%Zld6~(o1u;1v z+ED-HHG*PE>QUS?la)-ZXD|bbZqjI*!PzA zAB`ldVLoH7gtd)rvds zZ-dHxT(aB^dlpGGnkgUcc_K1<&>hO6AL2--#kVNui})ha6PpXm%UOJ@LB1zu zeGr3m5gElgPsE`4hz`$_jaStad9&7ucqtNbTkDq~VvGLENSlR-Qq0i{u~?oLtAeh2 zPvLt?FB{HxrJ_wID9@7@3cott|G5RV=)>Dw9R=f2iZas-4 z%X$*mlgJ_VH4Jk44u|vGZz$mzEvB!KVJR&-H4`Ov#8kQRrwsq&SaoF83x7(=qo^NK zD1_3FkZS6y{f1;OOiviayLobTcpQgrBpcjPN5Qnvw9QJ)!7LoKSlX-P4eS;`M*=Ii?jNPt<}+t|N; zZS523^>0{v$g+bReagoVw{*~O)ssmieGq#O+gCB@JKD>TqP>5Ir#X{-nD4`UX#fNi-~a$1 zKmiOSzyJdXKm;LO424C8I-h9c)yl{t%vVW>*L01y#(H3Q6tLs_Wt%9Fm#_>T9PSnV zYWAvYGPbV{W}?9oP!)wNUxK|7a?mP*$1g&>OBU#$F>hp82UP9I)XI*Km$B|%fcTWHLZFbB=h4%*t# zm~tXlK!q|AY_<5*`74 zNhlXHWUC6I*%Ohawg^o?RI~_ndD0Gb$Q3$)2upv6Hbkzf2aM8QsNSSkY*i&;dXN`2 z^`NMhVO3{RASESanvYyUCFzR7A5_Rsry5$2lO1vz^36a*Wul^@SkbDgTh*9l{R5*3 zGS*ad2SnfPFfm8TFB=xoMAaIiDk=(59SI7EYAhnBs<#>{s(A?cq=^utfr;)I>8m?t zrh0!&P)27;r6Xmjr6^T#;Zvu8vqvFe59(DBDLA9gOg}}FX+*H5d{P+0IL_3J8&0Ya zhcg-1EE=9@PJYFbiH0Uqeu+k=4IyGFB%xUhk#J%jl|wi~v{4LEOsJq3q6sO>5b-fF zLo}bUP)Bv6XhsuioK&mLDfDovGrQ2LYRrGJX6*2TG#dT$TJdysEMv?2_=1Y!x)>|U zM5iM1Ol7M4iJ0<|RZ%Ar}0iSNepSX4w+CzdFZ#jCB{ zLM6eVs=UUhi1QO{1-UXI3cBVeafzoYWwKlnJS2bAU2iAlSv~aA+X~aU)q`egM1+4f zFHYr`dVHo+(-a?6V^%&DRsLM0G(U+^Sx3Bi%;jx-s?UaRcuB1z(|LWe=ZMAe^;gDInJDuWVqe|kl;_Fw-OH@KngW@F`ncMJc(y5DD zKjB5ZAB`;j4?_+dl7^a4bAKq*cqY`y7-(n{UV6x!XhngBN)cgmE_D9T37%u=gqa45 zkVdEZux-LFb`m5btp``0@Qzehgu}azQ4^Ug#H{L7CJ~!6gW|cgk=ZJov0`}naI^-c zWDzKPNleY6ESHJ+L0Q3Bia6#zGk=K;V}Tya6PCI*2e5 zW{6?L2ckgb2YMgs95;x_VJMNv6J(Jw%0=zWCFQ6ulT;bk?Vc4QE~?l*PK$nsHK#|6 zpsp#R30IkJ#-g~VEAmS*HC-oK4BEs;rOV7nD`w)Kjtq(CAc=EMGU6yGt7FmtJUO)~VfWAid;&gj2KgT6^1Sh-cm-Mnq=HSZVrpTF_b&tvjt>IfzEF z8NJfG8}k;6D}5c6qV>!8SXb`RSy*Pq*K{oE8vLw(_tv5lmq{OxH8i4G%i-LL#5xk+ zgQaME50>-4_Fzf-RE1IWtfp?XCs%RQ&c2pb7SoEIt7Kv;``p?pq^s?J_bsKAQ<>>Z zDL&Je;xnCxrpIgTPIqX!&!x7y2NB6r4cD*g0h~qMnx~jX4m-ptlJ+zlEoyGU?!+&X zb&y@76f39u(?Sl_BD?GQy<<;eBr3vs>~~Yl$m})5=mw}^YB#IUsyh)L7a$4AEf+akI-{cUnY5rD`x0( z${XodbE;Y71^t;Msu0si(^Nwf-E+(t$*z$?KUk959H~osVjAm>;|JODjY>2&QuB-! z&*(m>@sHL@RA=|LB(qEOLzH~njtIEyd`TyJQb3y zE}3jzaD7#yH}O0X!(Yo94b?|!5UHZ>imu|v3<@jWX|Nm&*7QV*l9+q(tLO@jw<=wt zxdfvzsbwb8tERSpgkPV#&GZS+t78YpeyN^&9y5p4dCb$frSsRk!kot}50Tlf*+2mT z2kP;EUPjz+@E86S9saMk$Zzqzi&r(`efP$rda*e~ahX2qwB6#c?bT$jN)B<%#66M1 z-m9HoEt$lvF)eD*$dIL9r0Ml#!SWP!OgCOwS!d(9N=G<q!rW8^-9U7lnh68yr@DowX+cFs+O4&6ShqJQ;7?8QI%C?v@ELP zrk+-=5{_&)59!?Qv~$ZGHOlR5=u*BNa%whoS(MYs+RN7LXGcn=5oIZ(b;yhO8p+0e zMfr+K)RlhxqqVY^NV5UV7XurySz&@?pttUg*K!!3Ft7I8mML<)IPiZsZJDdy1q z$_gtJ8q3FIBxDKt4Ou9%s((ThM46h@mVM6T8E2LjZsZ(To+bCWR4J(nn#yb0Q7iA% zlH9vt89JS;>QrgsE5HI%~Cr3Yf)8kPK(x=oJyn9tc`ZTDySyD>Z*)awGu8a9)Itvq(~W`YK_B< z2P-ri5*Mma1a)m>b5=I4DyvB~vS{^^&4$b=ew&he)|p7fP)LSEDrT`}B|(=;YZ2zg zt-V(NKJlhT=M2S|HY16(G@>nhNM3h*N5jp!eJp1fq7Xzs5Ce&+LPrN=U0t2}M>Jkx^yBk4!DO zi2O~EQQ?eP6aIw}p15D%)Q&E`hJ3tw)E_xX$sCJNSKVecD}M=ArGjLS@jjPMg66@Q zOhF?ORs@Y6&UqxnurMh$Lz5RA(~u&fe2TBcE>tbEJ5NKZ*sOvc8rmpm_ZoCow4V6k z&7Mgqub4FT7=F?8A#7RV;w;f3nlO=&(#I$gQj@rz?I|2B3{!+trP)i+L9W?Z93c}? zA0_QU(%3Y_I)A-b;(AqHh!^CEEUL9h8TpDJW8)WbObq$gnFO-0!&oP>aP6QC8{;y# zMstKjIAkvEIbjoOh^qF{HXO5^2gmlE-MM(=y+|9^h@3(bVcBlDMz=*`E(=F4?V9g# zI!>@=kbA{;iC1RPe2E7s$d@1`h%_6u`Z5NUi%UsJ&1zTFGW_oRlq|&yy*dNd@8}x(RBC&a63wTWfCNxGJSJ58!(d^sNG-OM!qjTMSkY|?F2*_Rr34mjBL)(&7)oJC)?!sHu%e{=a4O9s zM2Mo$qN<_>LJcdba2h)@K|zF6foeutt8i#@8qrx4ax`9aLS*VIb&3VjCkr$)6Fd_< zq<;iY@Gwxwt4A2q<3U^uV(}NmeWRi)gnq0Bh7wH;gN%cAJZdZC z^-&8&d>J*!xMBqTovRtT-4BNy7VMrQeZ z(ms2B)FCBv;VeiucUAZp;1Qh+6n`LafM!C6IzeH^6BK49%s9*}a4Ip>7zQGWT5yH< zThe1w@8=vxk)W;HibfCDv?e0kwsjoixPd;Qr*CaAHx&`{M{OP1%Ux<4OUS3s8X+^6 zn1yy!tqUD07DThjE0uIHHJhmUR5%RxsQHFtu)LaysJ%?r1al7tsf7!})_*-mppB(Z zEh79SpTtTe-KeHLV^k}lsFX~4D>e+bbebR$E{!f-=}~j(l$cAO#9Gt0*yJH%L9fry zG~rWIB=p7hKTJkGf;rQ0l^`;Ct7WgPHCAiU57DG=GD=| zabEJ_e$hrsBQ3S)SE;ojFMknbY98V|3Gqv#5REHQL?JA??iph|%@B$I1R2FuMg*^F zLX0kl2JH^br4u~)bt%8Ws$fWPx(#Q+&Fo%I^36<6L>@|qb*@gbAd0-Sngh&-a>`N41JQHY~M0Lck z*Mf``k*HlnoEw7j^I_JVQEH{BeX25iWc`^qnCwcQFr;TbG8J8Qk9MZDE6zOHQ83EI zh?9n_bcqFdXY?fSc7I4{_8KhtA}LCm98Q>|xqq8))*4Nuj&aaYMtNMDA`2THgQ{vl zRqGb)MCtB6E(Yh(*+@Qui?!I1H|UqXTCwT0f+nqKMR3|TJgf00<59bk$@8e~h0Yo) zSmlapyy}$YLsVKFg6`h7uw8X!tqR}rX)URFm zGmdl58m4l^h~B~~k~>=xBvRyQq)hbHsZKO%BKry1d4Er8d!iI+E`&sk_Ab(VWsFdQ zWuq~CQAR;0D>%{7Swt7fDozoJCd&Cbi8{j=mcI<+EEnZUYpt8D_2%(99<|=3<;Xi% zT3I(J>saLAkR0?H#Hx7}<>2{ttOSwf)x6cR~XbQVz|=`CzLYUdPCR9Wk*_#030v>Uk?$$n4LlQ~sVqQZ9>vbro^ z(gb&l=b0!oL{vnFc<5Ghlq}c0xG@~2Y*lH75KaSy)7=b;FoHowmq=-g!%|WagF~AW zGLDoU6~Qgaar|qo2hlVWMCk;>Q4E@zp&5^}*?&Af>(q`af@KNzcZ~gJzM_jA+2@HT zjwiY&Ey0GCn{2VDlS$GpFf{h?YYGl`XwL0yz(>P&^CM?lTN(ASZ9?Jn`e-3IJa~-kBqEOCg zg>puPLpfOoSD~DYsEM2*Ct5C*iv}qi#t@@IGj7c`H(Ak@L{x9uLq|y*6UiJzMfY_o zi}Q45xtV3%dVUN^1<{Pp()2K2Y?)nnTIJx#xKSxpYY}hxZz(DTJET;Ngjn=Uh*a}b zsYRLMT$Cv(S2~=Ie+y14JfhcvW6&=Kg@m((kOY!7X3%BXWi{efCTl0^YEGs{`A3BvLQimlGm)7{=uJiI+V_)S=qAEhZZS>Y4 zC*<-?qG&;JMg*6WHEt!7V$tJ}MaNvKCWth3*&i*QjmG$91=^X$-zb;mzX2kDBVbX* zH}cich-$=*p*LvrqHLy9)S6#Yd&wdtbVY`rM93$I^g4E9kFk>OyKc1vz53#Rv%Z|I zU6?C3Zn<$cE@N716>C0`L0PHviIMPvoKtG5hI0xjELoS0(n?1k#mUUhZrXOHvz0_r zYkZ@AA7$Uyi0MRJd$|~ zFEg{~%ZR>J7Y2^xY|X@I4{t!|!4E%ur?bP!J+g&tLZGocam%B%<@bj!(9E2~h}HC&XkZoTn%btUe7PJz^Vu z-pZ*8p5uyH(1>c|{v)zx6Bb%k$*Q9|RkU*~zTWiVg4k43?k0SId?yL*EYE{@V{c0iK>7&=j=KfnY% z{IFo1c*`;X^+p1isbQ)NKw zdBA&7zA`)@z|fxoh|WlBXEfO-E-~mh<1USE4b+tUGXT}yxBuy`_<+Kl@qFAeF(my> zTQ|D?N(e65(zzjjypJ_MmFRi1hhODCU@QVaTWg7u6=E9d4_}%SY*5$FD z0mv0uGj)h>J~lQYj_#iUaGWz!-`ltLy%$w?SY`l>b=h#>W&jp)%nTL_hKJ8c)L;ex zEtM>B3z)LRKEf7Cw}#M5XdQF|#Q{&cPGko3g`*`P>&cIQi4?;y15m`@0faLH02vN? z^8TA@%IfzaWs@s?fO}2f(rjCOArT8h{PryDh%!5&5M7AaN8z#zhtEJuEcfQ{CRMFKGEd8?kghEOzQDht5kSOCy}gteE@)|wbv*K9UdJF0HE0Pm<5w?D53rT$AaLs z^|=6l{eSxWuP1>EFI2AoH~oK}gw^{&;{O-rs=M3zv4c5C4(rf@|A75(+Svs07oVh` z|6`TIRPNt#jsFv`QeUpKeCz!$_rkNh;S2D9)BX=i-yYA546*Um|06I78)ymE_ob!3 zzPS3o1l)6m|D-&Bl?g5x)G0t%;FJ`i|LPfkMPgq|I;qBQEMt^=l8az3bQwsB{{Q+O zrJr)4bgRw$*8;eO{BKTKdiYNSgdqosEuPPd-+xWkU-Tj(hqLbg#B!v`w4Iq>keo2( zx@KxYwqyUOLuqyO7WAX@Uh6+w*%}@gfvkT&7>t4S-)P?7tFkaVp8f~kI{?y@cuviK z8vR>8sKdU?*iPMSRChlAkpi@!XwN_6!IAO&Yr6m+-Iq04LHuWwlDHnr_|h;izJyvY z+^+kt3idT#dIJ73n3ss-;lB>wsg*_F;9o%Qz#bsF2|e`tGu`bF*YsXqM(F)1#KzLL ze~!oCTUYnLlal6N_ebw}0TX|QanKaH{nsz13SKMGP3`cM72 zT40WW9>CBlp&@D#2Ec}wEB(jAau*o!$>^ULfjH=l;~gmUUYv)`KF&{ovnj}-lyJw?DfnrLNr?I zry_bA+D{G(>vg5w)g%b*w*auj8{V^&KRFQBr6-KS?02!q27K)QNA{XeBR`Nb?7wOt zRE@TQQ}*=>msrM!$po9O-?(`PVqjff>rWCZ-F*rvYpi-p4(qSnZCaotqN}p&SpA8x zKs?Os&!RD*`d{Ke9HM4_)UFkP1MXI?U*`cxW!f!4c6C5Y)BN+1ZNPZ~@*RDkhj6YF7&HK~PVhwO>UcK4h(N5>Pu9UDy6Ga=B8 z<3ut8aW#LoM&Lw$qI%htt(>Yg+fyA_0s)H}uI{5X?Uk`=ij)>=emwBB3-9%U@mwhc zYA(Sw5Ho82>1;1(TLB9G(!m1VV;mbbe|WzF@K8@{nqN6`DA-}4cpZY>w9LC*+%&%@ zI~+)u8-WodiyD(n^WS@%&1VCfc)V@_fYULh`GfvugnF2NG#&k?rW)K*d=_Pze{fUr zwm`6cX+6DZewo@)RKqf)Vd3O5ipZi~tJTx|+nv$pRYF@aw|l;x<~I{ILiA;O6(M2*1}7bxNK z#~jsCr;Yu8-L2Sd2r`IQTz!PBSbpsidUxT7x$+w$GdD*{{;jO*?v}$P7%~T}*%8!=P9{yuQFb&Hu(#=%|s58Yt<%e^B{)c((Y?cju^8ZCq;#bZ~C!8fm zWVhsZrSS*hvxyvXwvqqX>7K5>imP9m$ug^NL@|EHwn>2X#OP&sGQ}*$fmD%@zZSc& zkp;A8_{4@>zfn{Hhk1kiASGrzfT@AZfIG-29Gd);ho51W$uPR1F zsss&xDAM@JXmhd^JdbRC!T2#tciU>(&lX+r?`RfXIZ1s{ad6@{j>_2YJ&X1tewpmA ze-Y5c0SO*{mCWyvep#@?FK+HYGOjw(WQ-ktR?*3%3XOO8Z}imJ*dZSN$7!0ZXjRzP zEwR|)&pe$l<+o_!;Xh_Mj)2c5>sjOG^#YN9!Myn4;b%GmEWmfmn0WZXpwHtWJp7sW z#OOLeu?i{CC#^2`;SW8()Bxp{hV_a2An3z?CQ#;t0FCd5AC;5sWKAK;qk#Lvui=Py zY0V#1P{-_(AO25zdg~DC*Z%zQ--szzrq$0ZltBE8x`VC+80^puzqzSVS)-6gW^3$! zEej#d&SCGuukknV4rhF^%xSyuzXNT-@ZJ~17+m=G#JFtI@ZwY9P`Wm7c}HAK<8T*# zC-#}Ev&e;iAGqdLDS<3T$L5pTNhB`(%|%LlUI)DEwN~AL% zG?2ZiHxJtEG5qa#?cc(q44VB|M9Jv(3FZVB+?qTBy`Ffv!S`Hd~ zP3c=fw}P7`F%Vc}_>(N2QQd(tlSW#ig~ae*<4z9N^~ggwJwY5;^!@ti&q zEAMxPAJoeG0~X_!jqFqIGyE)nmhwN6!&v-W9ytBk>xX+h0^j2V_v4`*F&X}BprqyP ztu7_hH^WcBcejSD83S*Ie-LbhB~WMP)E1cZNo4z{WRfBrld z{0AnUo~26IfnP*kY?hss!7Eh2kDxTng*_H8;W+<3&25N){pVX?>G!8aibcsU0)td^ zeV>2w&ux#eAxR@8lQFA7;Zf8zwaD+vA>P?GpnFs z3x!rUe0%r9o&e(+z#le$V*AWNNY+QYFIN;(nDn#}ukMFlmQ|1xu)?O+%*7jFge~ao zO^baA*xZLaG>2eD=9Ju5f^ZDx{(n#0|9$PM(ZsFIBV;uI_vbT7q5;ew%+k2q_c4aN zWa;5>+pphOY2j>gAt^4~XUOJ26)2Kxzk>>wQo(%V;_=kJ7RRxFOEl6U9R*fO`vE`x zQ__B}tR;|k?5lyv*g}4QgSExq>@yWK;6S-)>;=YUzjTB!rWW*M({RZ?kxRF43JWKv zKIPai*m;Xh`n@VA_OIm+0fqgRMi3h}oWB>AbhSqrFu!>5(H&KMeYritr0f+=2xh)_ zeGuuNGlwuycYRWSxk-^Ci(zH*t6YDP+LMdQ!STbbe+YvN4Fy)zwMc?Y2-DNMrsSdm z(8~J3=gmfghMg6x|Mjoam{{4`xcW@XV$Q4@o`lC&eZAgjjd((r$k0^ZWXs=*w|y2w z@uGIbSgo3e=WS>2;zanpl zL!r{2;>Xxi`Xs%t`hHzi6QfG{uLztxMkQKEWP|i6NhlQ-khCPl05&2{8}tA#c+t?HSGU!!#B2S$i1^h1P{VP={$G~65XoiiR|y)e*)h~NLtQ{OS2(!`6)g6#j`@0?%Iycxdo82G{wd z=If7(+t%AT|7O;Od;1Km(?2fGuj3biyQm<}fBW%&H5XyH5`?#V-^O*Q&(ORqG*PJwR`{tFs-{M zKOUpF$~H8*WKT_gpfe(cSoT@+QQgpp%Izq~j^wA)68~~k*l-LJWHs`og(QBv_9%`Z zqHKd+MC7kot{Ni0-7xWD3wCn$I>-;+Jan9YJR_uckY9g%_51NHO|w?yDUp0)gu5hq z{6gW=dUbfUu#R7Y_JqEYHXa`0_+?{TU|L%ua|uy7%(F+3Jv;GC)?6nv{tCX~c&*dJ zBrj1lCm&3a%Pr)~8l5Nc#cz>n7rW3kqjqR;$!qa#UT>gp#aH7yb|5LfkLX*3ToeXEoiSLBrX@@;fixc0BWWPVV;&3cE@sWiLiJ2#Z ziBp3)!ZWfqY3p_rA<1*~d+y(shN#K#$&m)oR+ZU4E64v?V$W9@cQdbhYKe@?UQ&6#wJCa6JI<8H;TvZ&*G5( zzRDrylKKO=jcAG3W}Wy34RVxnJLSZKNHQ{nqace->iGLF@b|tw585gQNf@C`yx^&{dVJK`XS>G^;S5NI}3g%eoZN2!yT3|4;V#38$-4YCq6hcG#!^^EEl`A z3SOM}gF7Aql!Wh{1U7&hVKSbjC0CsIy@Ug(pD6$F%d?yKa@nYxOH~Q>DLNj*+Fsm9 zy*Tl&`I8L+apH4A=_a-~!wL0zocL8!rKm1nsHDq9PW&5)ch>0;txIu#;)4_NI1pSi zukT5jF>yBWU1#Vg(&Kk3VPDv7bk4ft_YV=Nz8A>c-|mb={81lFp_yR92Mht?FL1X7 z{(J!RB}CuhAB`8^c}=r@x#4Tq3*jcTg?wja_|ju;*IHPe<}mIjx$rw#2^8ZWc|#CA zg?~FY(V$s+q4-MpIBn~Hdk}t7XUu)%ldjTjCz#g`{I@=XZ(|6WX_^9GWkimQOi_kQ>RJ}3vFX>f0sPfA6|5KVKbpAu_k+Qg z+VM0-e*wgV!>C%+`1|z3-z8Pp@8N zF3COlaz09YN*9Ro{m^n9Q@*c}F&>+zICN5(R)1N(uOnRZ5Izh?RxXVJ-mqk*1-Pzt zfMUMi))viet69x|d@rYRzbsnhBe7&e0}V~EuzU{%cUp^glrdC}d>_-Kf<;DAf$>kx zhN3up#Mv`!FwgwF%gFcEap31?kM9FDk#z3OEW7dXy~p#B-0G_RMgx!Ug`7!42PT|T zwuRI1*c%#sZ7AN*aD04U^|$a!HOTGvB zFF|Jyn75Pfn~I=w)k|3NeHTajsJutnk8YnCvJfJuU+7GV=rUP7{v>nB_oeX5cVcBS zwU3Tg~?_d>={fLEA0v1$=(G#w2{v-jzqunY}tqpy#=!!ZzkzPIT zYznyEy%B)rYr%4VJM_W*?3p>tv-#+LDu&E_~Kxh3?-phO%o;41s-ww?rj3{6Mmof zoyN)+d5Q28dGE$uB^gtZ#Xj$UuR`v{ z#s1q&)PXX}hLU{Vmk$XHdf(hsFe65z(0f!`qW9^6i5W^ZxG$E$4UoP-oNdGQn2of&P<7s<}L8i$DOfR|H(nf#&?USf)_oOC&|L!Y)Jk75VAllud zvxohwGSzU~{n;Fcu;!ZG%5G%$W0z;N)Xl+%RJ-ycwu1=cExRQIC#r9sZqdn5X8ci) z$^*xrvHJ{kK2d<2%zc6t^=bnk^I=SG%r3$d9iW&$Gz0|IwQ|JDF@QE_gDs$#Mw(B|f z%+-|FA*6`2?<|j9v6I=H`*sKxUi~GZ9&j6V#3D{-1bCi~$=Ov!h1&!2qY!M=6CAPa*{kxqX6G6P zm=B*NwHrzQb}p&?P9F@_v3+f2+mKP}1*|{%+8@(mNB>%X`&k4YWoi%{t-YUvz|7hk z91HgLq@lWGRr`G^4qd49w}dOyzS*|$|GZU&8q+@2Y+{m4Vfbk8J~mFCJ*9$C2w3(a z>u8I=`|Jr8wf_`20Uw=x{6NCDIwt4rKZKCHvScw`&E6dllx7cQfs5k0(d?V1!$5T5 zfh3X7FxrcM-@oh~69)8T?wgf;eltXuL*oPn{Yrsqve&#oaE{#EI;eWb3ABmqlO#A{ zGX_~yv>8Mw45uLalR z5xh>}x;K&Jt7KH6h$#Aw30ukoPB*|rS)pp9Ye=jjBwVM0L0JX zZa>O@ll6=W%lNr!|6+Z*Z+sP4PdNESsqSz=R;8R*FLk0yq_67Bkl@bh<1$+)D6jf3 zmLiW#QE8m1syyPcEdGJ7N#%zpb3&*-VX3s^f12&o z2Vj$y;p^U#W5dR6W$NjGvYfcZO{v#9hZW0ze+66tKOFo1m;c%|1sb!ps4ssq zw4uJO@HIT7)3EHu+2I?_#gB(=CKXbE-}Rf^y59&quYhL(r9vE}j6b7^o~r6=e>NJ^i7 z)yw86ugcm7K6W-$O?e&&L8>>FrZ5AdK-ETN$fN|e`Myng?=;oQGPst{p)}c1-wY;*L{BJ z+sv-MU&uaA8}#cSEvbnjH*aJ%H>oQVCeZT%iTvvaPXPK~4GHPlGI zZFcF&AI4zkT}8Xz^E9WLk5cP-D6Pi+;hi8m&jX^A6AE~tUh({p0^?M#Iq`nS!V652 zf2|@c4~>>URk0~~$?QC?)w$u#&S$VnDyzo8VJ)mWuS-fvD{3~OrbBqAT zzd2;5x0+jAh0c>&igk0bX|bX6%%DnIbEe>kx#qlCs}bU57+B0hie^#xFeb{~{0+aw z^8nlgY#+`WlNVQvevLDnuSO3NUzPUFe<%KBmK&~(Zob>7i{fQSEsc{_6UngD690A; zbcWq@nLsulTrC=HM15<%!)-dkn@CAcg` z=+Ff^&BV(QFdyO`Tl(#SF5;Qu5MLhcqr7PhI+MqPGVk)pvR;>1^E7?cdoHg|ZuTW$ z1H_AZ%gc+R51r8W37gy1^1T{|GZtnGUI6_ZCB*s|l%DIMNaP4Xd6w1Cm5<38GjSj9 z+0V(!|0(=y&<^1{UQ^|9X?s^Yf3xmEgo4WRBalc!0RJ*F*w{4>xtC$HJg8pN*2&&7ESXNwI&V)BLUO*pdReQ&d~ zmFI~}&QO371 zYSTsGm&VHA_)S{YEp}&>OIr)@*}T1j@#SlH{my?o`+$1!Ch(2Ie=trcP?O86 zM(hH_c;ekiwPx|KOLyxi_pW%_QzD`8%*^V8IjZ>NA~q{Flwd%te=Nnf{{qeqWpyqP zm!TrvNRtl4`>uffu@|>_iEr;%9~o6NiM-eklXz^|0(mIinrcTp3j~X} zd|446J5sZ~@|D0a#79%T`I)Kq;z0Z%Yd=e0!?zD#lt1#tJbe6i`|CW9cuRHooQ2qu z4Ttw=LWm6?JjxBZe;7Q&H@)UlqDOOKe5AthiXMRmR1xn9df~;arVhhFH%}N(j6K9k zzA_79eI;mD;amT8NpLMy6ds%X>s{qrg_;TfnaGvWNO(%8E<{o%v&eM_5uTD*{c8uK z@0aa=@S6m0V0oJDz^njGCc`s{HR16WKX{LcjkVaOiPmrMf2^c@`m(fmCX@Zj`0)pi z2ps;%mNXLXCIk@P*~iWzZZA1D5S~w_O3ckk&Oo2=CoBdlH7{%j!sj>kp(k>Af*-=I z3A;denkXNHUQlyoy#|ZA?SzwdxAZ{xs6CU}%3RX8Vi&{Q`yeY2Cl3hUfa+S;QA9@p z;i2Z%;MSo2#k5pcA}$*>!I%XunI z8IQFx_x~OO{s@LHQX_N06Q=$QN7+AS z&INz)LB*xE1z#5)-3i3y`X#qd&y}=QXSpvb6T2T^f7gN7f*()d4{{Tv4PDBo^km^sZKNfSiu1&_*E#r~cNRS<~_K44iHsF=O9Tg2jmFA}>M+_&=90g}Q6?^Z0n zi*u1*2My#AOa8|e{LNDNDp*_aV>je2<|weFX756ynfo>sDz<=*xKmsKgEig7DY99* zRSSOikmIL$CvKe7x6x5>EqG6n3L?p7!PBkIe@xsW^~IfqY5+X7;Oj63gi6kWzmh4Z zt0@l)o-6--N0C8NWiC=reg#k5n_ObSuO6JHk1sh43;rT_Vk6&6+Ef^U>%B9#BI|D) z3!W2@YN(OFbez%$)xd&3o-*8r1s}Vd)lGhtCfHGhMkA2`1sMWW~#g~y!>W@7B<05xBV0R3s(*L(40M`wFs$di^`fNE& zkJ zb>FS@jz@yycWgyTJ}<-?2P2!4;B@JbHuoQ6?5yPGZsheW~= z5g2rY17V(taDLa7J}-`MoB~=I0!*%-8yIh%kAaBXiGXnYYgaq~!LekX8X{*5!tvBs zJUrihwL;hqQu^iP?0uIA!Xehze~lM-?z&M1!ih4Nz(J)Hj&HUuQUpq;OhR}8x*O#a zETjK8u^PZD!nhq1&X*XEoc%$%<9|&!zUKm7g_ zHh10wui!BvU9MPsz|}Y0C+=R~l z^E#Xa70}d}GwNEcGLE!0E(k)i88H_cOHG%CQ>wc4dJ#e!g@P2Ff0iFQHt}G@*|Hhw zruparv9RAJW;o&TcQj@iPVd=U`Oebc&x-xR(&J9XU=0;X1A{iBFgCh1GVGH^+`l8r zX*gGs@q7E;H?ibN(I|%hg)@>78qWA3ywUv@7Nyk)#SDO0S@6?l6y&o?I|h17kdrRB z2`W<+hqYeGC!nJGe@#y)^M8Qqc5bv8`4H^t6tafH+qY3BsNPiw zH*Q7?ZeQ`?0NvN+OqkW$@XqM2^nT7Q8~7>>2lk58JyFQ@0Ss`FDs0}F`45B&2Nr*p zhl(b)-Pgn^BU(y`t1!|05SPH21m8x2135LHe^X%;jp;f0e?~o75+uQ;Bq(G=!cGng z8XX`593L2+^>~#bItv-maXhp0k**13t2cU?1kFSCT!I51CAii{&df`2>>lED?Z_%` z<5DuJoSc;Fx#j8wA>sKPBfqqKWS)1I8hK0ZNpTvGOGR~!5VhCe=EYdQKLn4L#l|CQIVj;y#xlp zdIK&XISARQw2cFOc`o3rMO_>w00>#B)vZP zW#ohGTfl+DFMX_xU~a3GBJ9SQX*$B0ze2Do(aPZn=Yx!iAjioFadU*DHZ%u1#B0~$OElnegbPxJvtc9aQH)9)4{AVJSg%puygi!CJ9h?0~ z4>AR2M0#L2l0-0qul5GTlnbY%jG(>1rI;CKAOKNDm=Ww1W-Dr23xpXFEJu8~D-m>y zKn0p5>b_t(Gm~(95wzn1M{xx60>Tz-;oyxHe-_J>cB(BL!0y%fh7;$NMHP#~$!)bZ zDKqYs%_tz5)?FZ~d+|w5SfxIOgYmAy*9_W;;p|CC(=mG!)ws353D6S!6IM-9#KU891Ja{${E9X48B#3#Wdlk@i|N5mE7z$@C-ZX;VQulK73t#TaO zGd2E#%#D(2uJ>}{xmOu;05j|BnFDkZu}J`nnH$16$X9nF5GSaOp!_Njl+SY}eLp4> zA@Ot0zv~}S&{4%LH*@He!#GlFgXyBPf3QY6J%kXnJWqr=3f~}fvKy1qag~e|GhNAo ztx~m%>{w4c{G2kqLxu(R2sgt}#}x!rcjoCCBGkEW=n{2+Iv%9}kK8OmO6P6SGzB2% zcuR$r&VCwcEvu8b4?5Pt*s8U405k~~3eq}Ek6*(9JM$)!-=L0YS?~}gJ8A+Vf9YAw zk?X0X9TeL~escw_0n&ijoq}XXhbEWyBid}aEgy^NFVtytCqH@zly-*{k2o9el+LK% zkG-RBr58B~67aqBkpw9YNf7(*Ts4QKPw?3Ao8V1w4aIxQi)#eQoG0*8K-#vnQ_u2| z0^=+>wvg&4P67mc>mZ;~@#OO5e{<5w%y@Sj;_+-pqh%I(Y+;Ri@?hCVmIp@i z?N#p%OMfDHO0^pRib(cZ^vN9w(%Uyz@d%v_&~qRZdJ=^k^BXJkB+Ta#czZBN^u%K$ z-IHAw{F6X8Sr44ErMn*V-zwHT5{lDy1FtNmhw|M#QZjeRogc~fgZ6jre_7`-U-R$i zkc8~NEOhoFsSJ>$$Gb|neNV6?n0d5kJn$JHC<^W*nSUc1tma9@M@Mw}1&+*T9nhQh!B;|RZe|i|5(iG0RX_8{c>6jQxD!9*#^Y29{V|bO67oACY>+Q<0 z{H9Eb7HD(+7+_pXim#O-xsT<@>tlh3C{+#USL(f(l=QnUrHU{WOp5E~=CmJ_&@AUA z17~n!6)22C7jWofe=XwF_3 z1WDv0jxEUaNhY7VWsH2y^ck^L4SprKKVsu0xj>4$W$8LJJt7IRo$W(S#XKa8 zVgM$^#!mrU52_C$xY7m);;ds(_0fs|mDk5xTsfJPj|lG5e-A%hvwJ&(`-HfY=6FQa zY|f-nf**|X!-(GdJd^0SkIK54(K87lnR#?k@l0yBz*0)ODwy2IHH?Z@5*}a+Ecc1S zpTeiQ$nuO%a-YTVQ|W>;raF_%F@UmrZe=2n`<%7}n0y>kp$R?oILXw_AN`XIJ zPp#vVRv}Cue;H?QjT(8+RX#3)bxtE4W@wE29Jm{0l`zGfNeb%dnl2yfbZ635p%9}# z0oq6QlKlQ&2$4`qoma?W^;TZXJIW-83@x&@En7!u-qAk9 zynb;fYAPuWgF#=H9NGtIWQ`80|AW0Xxe|VUy;2#o83#dxJ+87`z)YztelSq$!YHHtA zm*EtwJ{w#?6aRA_c6=mr$071~Y*_vqmyuBJX$nVIW<8p}VA z>Bi4Ns))Od`gHu%E5bhS;`o^hQ~IpxwfjlOPx;`V#EsNFtja3sfua->qg9}=d zcK2h>#U%}tekGi<;CY%t)ZtG_TzI&`@SPQX2}R`cShxsET{ScoWq~&Hf9?H<{LJ!T z>6Kj-p!bs{#ymu;{NVwVdiYv$m01q!{TR|Q)tTg|0t(0#MS}47^|W zNfdA0xA?Wz&+ne}5j$cOe>B}H_oO<)m&~xC(1?^h=^ZqA=vg96uqV|i;D4xq+phTZHZ9Ym=qD+Yl*~R~pXASrC-^jUhLcx)(jkMCd-8H!7siEth$jnf5d~XV}58hKsPc3M?zvao)rE+(-6}r-?)ZZERr?m?BmChVQ-d) ziEt!rPdka#kMutO_(|73bpf|}YfMMr$2bA`3e`Df-Qsm>uJs<;Z+1U!; z?IaL4@e^1x^!UM{====xb!Ex%6QqR2%ZwR06z=#@9NV;+Fxp9oBd>^#zH9w4hujAD6ZpYJl;M* zbcS6yeohBn2nnYRYI2fT z>U4_-oP>`Du9q4bFb8wmN7!2eCmnzsf92jM>5A}^h}?yRw?wtzNrZXig~Nk7L;)6wCR zt>?oJeE$WuTMK7&lIciU#wJ#YPI~`0rgimJT^Ml;s;Fk`-al+;&ZoXf_RV=|^=^;N+};Af_lz(zVLcpfoWf33{w|91p_prxYC)o?^~5?P9)e3R&;QilaM z?gldO<5p#_NO_+-fFDZ^=_I)M5L1FjhAdfw9*;>Pr~f_;?ZO!zYNuvWHbwQLk}DQ} zpHMVMduLBl6B_MTd18N`Lyrw8RcnZ!?`}76*9G6n)L#Ckvhw2Rl+yc_pq2!Bf3-U) z?_r%836wy=<*oKDe*TIkkBf__Y6aX$J{f9e;zLbP{8X(6-rvHVL^CFykjvlVPBPv+ zV6Bgx{*n0cn?1jferFloBv zM|KVlc54eV8wWfSv}H-j&$HzXe|K@Qs?p5KliajNX9JcTY4fCMc8P{Z0%1h=JxTOo zxg2gp$H|Xpfo&8(Gy5zBCqJ*=8|87$q9#$3pQt{qwWSq$J>krgl-^EIdzU0|nI|P( zHnn|f`I#WPlYV76GbcY@8@+(@6TOfaE&ULlqgzVpxqir$^X2>YgN?x-e;O>k525_Q zm!?18wm&2>I%?rsqGLt*+&`I9aWj{cAmpgH03h%Bby~ZW^q}7?36Oh^Y!1*x3CN2k z#d-m0Nr|=!4k)EwU#`N#3k`xhm0m)ZuIbm_{PM{KLaAZ#S_U$x0DQZFpkjwvF!+43 zNyvbp8Ikr7gI1R;(Bw)-f8mgM%zx`-<4boXut!1kJpA}yDUC$jUb+#>=Hyopj95CN z12oiF+AYu;ZGnzK9tSE(tWgqiw3k_yLMO-Xfg%Y9`Ij!$!a?3a#&kMJFE@fjd5~k^ zHFkJf8m;MwP>z#w`ynlb%%P+BS$g(WoJ@ZL%@=Xd!z`qwW$8gDe|N)PevlSGkIyd% zg(huF0(MIP7&E~9Ej3)GXsLuQfK!fKvRjWvh)yWQzWy?XLa@37IWj!oZ5`SrNtVZY zmjDah+;?F0UTA7(q5EjV2wbSh3V~=fB2N%@ZgX;ksYHP3Ury}=}>hcXr4Jt7ByJFJKkfJd^{?&qMXBY z80AXDS~`P)c2}2h8_#aJLqAkW1=z6qMJ)z2yiTZL5V;}hg{fHv;*Gr;x$?qf0;XB! zo-IgMUpX{CB^%_f3QfFnnr>JinZJTpg5FOWP>NTxKR6Vye{!u*3JwjIGs-JFAtmmy z68XYPfI}yAa%i&4-VS3r8;rL@@c_fz4K;{Khbn>!Hv8?NwsST#pn0)$vxG#MtLl~+?)F6cE)=oy?X;svjQhn;2;9eb&MH`+n+PD4Iuf)z zJMF0=W15}`i6)7qglR?#;I3#_;DA_!Hu9deYH9kuyFRn13O!J=)gr=%HRCPH87_`F z7Y#*?NPWCWnZ8I^Qch65ivy$SOuTn8j1DYiTw5`6e`ZoaeobM|GZ#-!FBrcnLHkBK zKsF^HBDZp!5ogGkh&DZFp2{wj1=S{MrGvv$BR9FSEK_uX$mrDQ@du32w+eXLf#YNZ z-=rl(M$HL zmAX?VsZA6xdlkh)zpVKKfHpnn*`lEA_8e%WlR2xo9qMsFBM;6v+!U^^Z~-)OWjBxU z5XLxI1R4ST!RVtM7i3`*(58nX>5{9?ggwy6e>y`X-+bL;(5A}4f>?28Wa17c&?q2R z6v*$$_0NclSfDx52nkY`{9>EFaKNV9J+`SJP|#zIHk7D*4L1#Y4&5*}N%TZ1EH`}- zM^AI3VCNyXyqlJTw1?00c~g~KvPFNa!8iHJGe6LTah9?W=qSq|cV@xK)`0jdc66nX ze+24|X1xS}Fgyy?X8>HBw14JnR*zzMydMo9r|Bg*8MEI0eS}Ln?F{B4%O7d;#Ri)& z(oaTjGAD4F{4gy*T4{5{cvygB3YL^_eW6B7=9F^_)u>Z@26M~_{j`RN|7z%u0L=P5 zq~zQmA+T;7Xb5`B{)i(Vdg=j#KaDrof4Dy0>yo|2{ZUReTXf3)Sb6Y|Og1$$cFtP1 zoXDG-?72nC=Kd%OcgX>FB=X$zUt|;0#6aqkhZXFp6nB~vH9u1= zyO1&^nAK4igZUlwI*b7lXIU;8NCstf@>y7@VSseMLyW6A1;L(zV^fk&1f*cuU{RHY zZW!7hUd>6Vl8q={y%kDXo#wOPq#jWIXRR~MiQi5&CbLgp4UlY`CT%Je zPe2Oz5L3vey!}2&K+@e0<(wX@S;CTYayE@Bt8e`CK~RkC0;w1?TI&w{vz*hL(*G9# zN?2YX>3$V~n1Yl`NZUvUBvFu}Pn_S&c(m_1wc;}D%DA2_cqT~nCpA~@e`*}xE6+rN zWY?3MWDAW1DFB5x@yU`ekoBBq!H>$9G~KK`r?h$nx`WcQLF&YhBqYdWpF-(WHeO-L zj?#%q{?%MYyI+(}&KzIBW)N{@3+aX68+8aY64GTs$FLi`$Vg*KCkKuU(CH*W&MMrZ zH0VOYWeNR?8A`-pJe`DVf2Ju!9?9nEWK1$Izx4f-G1quHO(9*&8$UWPPp5JKiiXjp zCnY3rPoO&c^FNhNS|LUvDKM9in*59kE%AyMqF(9LAjYsnZkvoHB!gW#9DpM%S!>NZ zmXM~9u=6OT^+o`#gjAGrG1Tnw&!<^J!W%zn1B|ELq!UzntG;@re^qsZM8@t|hc~@; znW90eOvw>aXSh@TbUJS`q?BiZ37{rO&EW{?jgy#mWrzC$p>%?Rt*)PAK7g07BcwH; zm}~VJBcDbiq~tSY%u0sRqjXX{K-q-v`(x7}-5Vc*UX)G|Vhx55AWFasN++Jg!4GVp zeI8g-LEa!$tb5gce?hKokVYj@!Mfon+1ok2F^m_U1f>gN#pjv?5shuvSVrWzrYFfMTtnmW$RQc)4N=9 z+R8c#l<$q`2q{DWEDOmKakYko{h~_;*GbHIB6HHBOlx4i6ElP&Rrxh7mqqeC>@Ir; z)B1u>NnLj&f8cdUf}{-lJYOUMD(f{o5qK2y>LjTX%w3@iXFi@J)(6@u$>u5Mv?P%m z1>g3PrVN^`Vv-lDJtb!nfePY@uSw1y6~6+U6s%5?KiHioS^JDSKgrlAB@{4SQf>{5 zPxZ#ah>KDtKd~g}o~P&2N05^K>BysxlA7t$bN*v9f2ITq&W^=(N~7kN$)S?B6j(*B zk2FoM(@(laX#z$mi{vT6X3`yKx@^kgKnC{{caW{pQ|9D2mN)UYpPC9_!qaJW!u`Zy z(9U}Q7|&Iz&u86|=_+ZCt7Ix~<(!9o_9s^<{MZODDKA$E`+k?A8F{%%4MXe4mOmYzv>J1?(`C5M3G6!7A|@!n$TqN=(OOJ&k?#0gN$hQ8_z z325;YOEr=e$N8}q#uZD5H4cE>D3(Gp7#cz)e_sMr7yJY=B@=k>yI#do+(3;GyWt=% zOL!=X_N0)g(MA%%lI?{c1@C)A3KXPtu!@IVMeLIR+8CL^UnObTFe`JKTwuvf`Wmow zf1@0J|4NAKd)$B`@LmG%D2m!}jkdG1#Vo*R;!eWkq>J#`rlfSMKaX-9j8F94vrKQThG zt#RRidu$rL_DTM*OI-k{f11(T8v{T+!O;>{5UOb6=;{PD%EqGS7?N3@tw?m;!oUtr-k~^lFL*|O%#DW_ecS+Y6J5#rjkR^`b%SF z_A!<4@cV>I!g&i}AQ;%uIYH!GVzoj|NL6A$pzjY-s8W^m@dy205CCL9eU9kNdTadZ zV0{r9)#qjiAtEWIZmWs-U*E zs56KFLxB=GHtFnA&@Mks;-$27N%(rn4emHMX@nNTb+g)vRJe-0+DaaF@c zO5si+UhV~A+O>JCCdC969tlZNU(}9C^(JL{sUWDWOBM@cZlQ!9iVmu<1sYy4BJrxE z27;g*ER+%X8hcDK6lBk&qB&aPBMuE2PO%~~&9^aiLJH#|h@@q zBYv&>(AE;o73L*!Sln}S$0O`@k;xL5A@4@A$CKYi{<^RUi3maj!N!@dj7+!sYPWH@ zmA*-uT*X(0>R=Po+z8_b_0Y(qu6I|_L2G0xrU6Z0q%u^(H-n_(f4WjQk10P&qs-(n z5xcv+#wBak21AuHyTAimM*<-@M1>N#3IwM@y@voCVOIP8BpiInYbz9D`f489=%c@1WHqz|(e{m=iht)N6xl=Spd?}`Xj7@|xSY2zqUm+T8z z)Q*LST3ctggd+L^ zBT1+;SAysI3txCJX9@_`6WdUbHe_*0g(0eLLydNL(XN#lR>1HcT(rapolX*=cL#Wr zhKl{x)2duTGJSKMlnI--4ZgLWy`)S&G$v;F-`MXZsj~=SL{>kg6e*JhN#A^LiXm<& zpG7Hge_S$6WeLP`X!d5a>0V>QWU3!!zz~WWs>qCqqqj&Y+<7ote2;#zGB0F`=!k5G z7W&PS23sc9wuoHRsE!dbp=p$ZkV!!cnam^udi;esCl+<=OrD*+$-?vTe$QKWC)7eo(pvXnhVx3UzMVZBm8Gi|* z);WJ3zlk*KIKc^q`6c83r+OJ&;EaMxf0|mGHWd&vf;YkVJPh3YaZ7PdU!1sJmpWjZ zveHT78|Ybf>VzZLX$BkaC@VYdqT5NchWopdM?hFFEg!-B8y*-PE=1}^JPknVogq){ z640g0Jdv3NCVCP{DhU7qUlNflCAV-kH&4JdDT#mb_4cbL4Bq%iT`IR{A}-$cf1-Md z9Iyzy+?P7cNKpX*kwdm-q{_v3&U6YySm^1=gtrOIk^qwWPh4u% zoo`PLgI^0AlJW;6MJ+3e7fmw*0!h)54S-L8eQ#s=78;>0n=8iB`W2pH=6~y zCqnEm?@1|nr)|I!rxZ1{Y&cLEcYh{EVJtxfcurWV3(5+k5e!&rpycrn%_VXcgmQzT zPc5anO1>*py`h%cQBHEUe{Lzm6C=b^s2CSRi4cdP?`(f(vQqvSitFBktilvM{E3F1 zc(sXoI(&AbAGGBUNtPn_5e=h5|g;b!tm?jfI z#A@dhnKK4vl2V_g3~vq;B-tNn5l;DJ1C+s20^A;rQfj`H^>unGZ7`XGbHBM#6n{`W zUzWO>XW}aao1S+|xzwn%`>8dl6j6M;J;0PuH>$)GRr<_$5>&<%1JrtlE0)!VG<8Ls zyDryM(jSEAE^e`VNF=vktA*Yq%1Ie_s_Dyk+=ZGD%}*6tqHoyusHP~mN!0^Ga7 z_mrNIyc>|lk1Ei`zP%{9kKiO%-1!R|?+5klqBvkCQ0krqHACbI$o7#bDfaELw zzfk7M(gP@b1;cGok@{C!$Cz}%MHR%P$VllT)7^p4HsVo>e-gnMeHq7IRD6b}6@f50 z_+c3@Dqg9Kw`aGYPF=jHE@LSToM2QOqIGer*~6$h#Ng}}CJ zC2UD!68tZ!8)x{u7|JV;kW)d~>=N98eezQnA0s*lA%Or1GfBGzAJ20aH#mK1oK_PyEjCZ0Y z85P#SW@O$BdXKXPppf(gcXx`~Q5h9Dee)R==GKTt1sb2zsQiKd<%Bi}4^WDtMm1u& z)(1-^EN}m(%8}-xfTJhJPL=+dtMJnLP!vPge8N$DRK-idumR#oZ6B4v5%JCKMcf5+ zcZT;ee^ISM6hl-pb;#|OQ6 zRNoGq!Gol4Gzs={Z&YO9QQ?M%SC+kQ?Q&fXz2H$%6Iq<^NZ3&YtY;3LyKIud%pwKb z=P<{SfF2lN?WoX*IO<=*`Z3PYCDw}E)?_`qfAYq_AiSo4@KD|2f;b+a&cu2mD%HL( z0{IZt;Dt*DBlghB;!$B!Xz)8|Rjn02;5Ea7=#y)jGU_mLRr2*;4hGMOz*4`0JSre( ztwjZYQYs{A_!)dWDqyua(=0I#f=30Fpi0{htR$WDs8HQJlrX?xlt%^B;p9Oby#(>7 zf2_wfPVcA~;F=bK@~9egb7S5h0)LBaAM6HCGg^kg@e3RAr~(N*5jJT8^E?l(m4%bP z0~ABY(qGrZqcXZH;VH|)mw7plN?`%>lP+$=fKj7Q~7e}jbkQ<0J=KRFM?N4O;MeRx#=P{qF(%cC;u zND*U+nNbsu3bZb)9KeH1WIrXRP6Ir}DvBQLeB)8!NiYCDBq8{!e5Ifm`H4;4NhVP1 zYH33py7Q<$qw|ypRdkJX4G)rVIEWOBf+&v)iNSK3k^SPo21M2$Y zn@#3}qiTbUJC?5a{r!gAr~>53e<;JbHOR4^m74lPG%dXw6<|0-V{ud#ew9su1dx1* zPM}nARIOAw6yE3LBgE(wBrZ~ppl+1^&h~HgpV1UY^`mZMRUB1o&W*4fZ&d9|rWDaC zbT=x@lPM=n7^Zil;vME>vrD2waF46h|5 zg>h8PUP5kEuvP<2Io&s6Z6eV|bqMKLWoO!`tmE2cUwT;8a&kDZ(GG=+ zh=WCLP1>kp_E?{cp)KN7HY!-Q(?Aw5zVuVbAp2hh$*t1HMrDV|(i?Lr$!%2N<#a;d zGjeC6g4WtJc(%?^Hmb)-f4;^>;q8rTuEK~-Lj^?&`ma#Ie3rpj#?!vusJt;I;~g{0 zjp_pY-oUU72{)>GQ&*}3haPvMI=JYTVX=J490S#ER9Ph0HY%UXT8-)qe~r)XRh29u zjmk!v057K+b=WEb#d2KEV)b=vRE8`_DfN{F_u3n`33w;hsMup)e_y|Nc%piZijK7N zs^~INQR}2>P@;cO}%eSs>h~0P3HODVZ3JBEC2)+8b5d<@7+Pdgn%^ zpOs%5714H*Qfbjfb)$&17XVAYjmpGBgp|;FU?HYISnzW0|EuKLSR+28CApw(2Q!Qr45evE8s9+b~nv4XY@8N z^W6W8OOwv*r zL!P=U>+-9zPIyQ+f544VSwW4*rgPcNd&j8UvTOxlY4{9t1=GJJ5u^H5jLK#d>=5J;qY46Q zxJU+5WqElOGBGOGy1y&&yA(P`Wyqy4cNc|O28vP15;V7q8TXXrJU+cT@D|zCK}1HJ zWQ^)O)vCpB8KdI$0Y^sVhYX)lT_bh`=yRl|B~7EUe_?B-mh*j`QQ-~9yy;)6XH<`o zy+N4+uygi6MgeEMpDXFtFp5dq?6( z4+doUF{-2+WtxAL;D<~kMn%sjLgrGNY*)-Os_YqMS^vOFm5iWu#LK8ISa$M6qAv@! zWmKjeaDkjOGHU54C0LjyNP}e74q-a&=!wf1e-&2mZVbFI5NhGKz4)j3{%MSAQrmkq zFH4^r7}Xk}ReXhXN>za(ZY9E~fGWJ=iD6Xz<;Ya^0Pr1(rq3eD?#9Wm4#FKqWd|U7 z30S4~Z7DV6BcXUO^Drv2FXC8@VN^9667NnA{}tg|Gfd8R@Cu_^0YMwjJ)DL{V&*Ap ze>aC7jLO&yyU1WvA=L*TNQV6$7K+&)k;DR5TL?x4nuZ;kjQqyDsOIZfw)9Hc7Tmr+ z&=fh&c<`cn_SWZ}1||d>1gyC|+%uC1+O~TZ97KhbKTF4SGAoJ2TO{oZRpCYDdTJZg zl|Wro5x@H$Kj^qD%tbY;=s~r1IV^uRe+0$SjpQLJ@>L}IE-EOjKf=0;DMN5ksYqY0 zjBrswWP0DgAsRJtQCS(NUZn}(7ZtVc6m%jkz`c>CG|`I+aIRE(%{kpwQxN5_ zHQGhhzF^={`QH_V>7rtkuF9Y~6-#9vtzp1INc1~c zrlZpCGJa8MA5XMw-e;Ys*iOZ=f9uRQrY_}5gnQ)!7S%ct{V`rt!-{Abl5RsJwRus& zHx*FfK_K5KRabHPi|W)fN{5tT33wfLKi&LAm1FBgj^i<|vKtusbo5?S9uqJ^y6z~? zHBWuEEeeP-{|RM1jSODl3XdU*3LFd%3U8ayXtboHOFF8P)L>INTY65DGvSKLc@O;H&lBi%t35G zG%9eYtQHy-H;s|T*FYMTxf^6XiZq}aUT0KH)IJ1fRK@{nFkh{-e;JkE%-Ui`1-uXm z9O~F~5&&{Wm1ZvSI+4;2XjGot5XW;q7T-kjgvSBEixlinqr&HWNK*_dHWfG|Q=`(q zF6-&Sgj$Viv55}WfB`Dv)*>mpp)m_kqte=RIU_-9?rKy8?r|e6RD2p06kDD>XjBY` z`(jggLBlE(#+3>uf7%Ob(q{Kvqk^I-9f!`vY*e9{6a_Vb!t`Xxwr^BOKSIURCn@rTTGHby2Opf4Ns%b5vm-F8DpwY|TN> zu+Z}%79w>OPYEH_3r#5idSG8=0wAP9kcU)|lM!oSa;<_QvAGqGSQiedXkQ&~4A?l9 zXw74)WU&>1_2iYEsPX$!yb&tiiB-CX9o{37ds!0Qp2LJUqBMA;ZprTl3xwmwAB-AD$A zxMtl@Q}+z1?9W?72_I5fk#LqY?fnj^BGfD1QnSS2I*MPJmrs<&7pc}ft)@3P;Pz50 zQq@ADuS;+`qozixa#}$pq{T>O9z1nMuE3WKu9O(5e?a*FSU{)0P*v$P@*?v%y$bok zW~73qj^hj=x4;0)B9*-qQ!d|%R3#WbxgQySBtwhsjS04eNU>bS9P%nvR}eH*ha~>%_>?sWtvnCc6>ud z;y&SRQbAsN3V)GzGL!|NpD0bLVj@-1fagkoufV#K%8bbHTS8FD+#LUD$ip>-$|TQ0e`kwtBz|IClv#(8kGnJ)83vI zD<@T-LA0+~E6?(haZ(i@Psv%CoK%$c`nU(c+?-TRy6y;;DSG@yV2P9JjF?#Csgtxr zvz%1NJeEE7C34G^<6I0*s!Opp+&CMy)Pzndn~YPcO{=x zodgz-L1AJ&@T964xg>v8^ZTU2jzG`5vN*4i50vVrbShF+^)`whDAgPQ`FnJewofYh zRCwBHqfEK%lM0H{iC7j@aEIkdH9o~{hFPKLkAE&%XTuTSIUwdzdO}Iwlj^vm z5C)~v7}Wxk2#$m?gHoB5r7C;Uwi+4$rK+>KknHh#UdKHulqy!ct&?%I;7>xST!hT@ zs@D-RlnUakgoZMB2WmvAA`0oP%;F{|i7Wd*_()HZKRQa)XiOoB;ixD|wHcs4wuGb9 z$ba?~ywS0od?ZR0diHu4rTU!Q3(E+-g(%g%m`{*AdJ?4qm;r*1Qgye_*c!5gaWv@a zc27mRe{oT&imi5j2r=D~j#A0^ne+Rh3=|V6N)_%)=zdvG5XeiEipClB)!g+$sbKc^ z3128Q4N0I>38LCRdU_lv)#V55D*eVbD1X%q$(4)H=>DKod;Ka++WpZCD3xf^ooR6# zr78*A=-e|%Orlgk&xR{TP*AG2Zzk`R01l;s(fV^v5Tyz^!0jFar2<-n&vYAo@^^q= zlP?oWb&;tUj_uLBfKv5yH-AK_m5R=CiFv@K7R4b-K4T<^gl8u73C%1g?4I< zom3-Ka^G8@^F+f=Dn*7lz(seYDu2qIR9A9u40boKbUvZmj4#`GiST9y_!|RH=-x2m-x;1Xiis<)Io+tW{O23J{lg2H&Al)t20;qw#G!^DODrilF+^gwrM(?Qgs>wB@%Uwj!L0&vpU3vd)muab#`g!d-y|5$rfsgC+bu@Mkrl`87RY8hF5^+?Dr3^^m-vV>$|L4S$d^i}}Wvfy>G@wXUZ8@c@Qt1F04r^2@ zz}h|h9_C1s{3=z3er`!FSAW^P(5h75I?CoJ6WjwNSSwW?AGS+hjImPj62<<=rXiPA zslZXUEGSmLy>63ivr=`>6R9~%LU!|Q14xf#OaDvLVWl#Tw{pXYwtiTtynpQbIx__a#n7!( zb=^$Dm;kKW!OZoGb7N?`gv88BMM${Agt(NiC9^F_5ZtW;w* zgRK2ls`e2-YO0VjBX2i_H!D@{&|vHY@TUa*-Ad&*<*xKvKgBCm3fj_~J^KW%RAZI^ zvFNlW^_9xr3*R525xA>=9sf2Mi6KHZPl|bmIO?lHG+7PH4EV5G3{P)2y zfsS1USM?aa&1+VwStzTgW{_JqO4Wiw_ob?h1XEqyZEkcz6~+eyQ&DGJp$zlmj)+eI zQw^x3X95aLb$^&vT<{}C+Zk#18%%WppF1!Y6BnjZS7^ThD#BD3T9PTm&P$lews(11$f_7VY}-jw8+a#G1WU|>VG;~B2&@f44@8~b&(4EAc`|F zRc^GDeA|YpDz3X_;=;mIoCAb-4%e{EQ;>;|Z85yCN&Hh6@8z zjkJtF#~7ij;Q(+=mh=GV7?kWafkt&pQRSMpH?1)h}bJ`2V_hOtnv| zNq_7o>A~c0f`p~78s{+;oE#l4qszjg!U{T3Y+7eb1(y45WHP1-G~_^-9uGfof3$!d z=$NXCYFw<_|ZwFrz6|;MU&UUpl8V6%4vOeNY{G~Xu zK~hZBVBh~i|2V`{+d~$K1DF&-Q4CzgIbmU{TI9nWh1(TBHi}l_XmZm>n2O5)vVW8E z*$!{*IOqorG)zTHdxi~hLZ)r(eTYzdRS)?%5vDq>&%0tw_0E)Y8y~zaPo7WkG1V%_ zcTs!qZ%oC;NQv14wrNcDWJ6TNbu4aFbAUWYUp1z>5MV1;rPJXuUs~NdH>SFxah!2X z1)4oYri`h0JV%sbhTCK+#hSAW8Glnf*Rc9_nw5aJCsP$AZO#*E6Sz#(qNmBIrn>Wv z6GE!jG8G3o3&;bVD^tO9$*g3m>duH-i%f-Rxud=^75pD}zEUnz4@>4wOqZ#+objq# zq^&Y#M3fIYv&vMQAY?w!{d)^Tm8n>9q6n49%v2jC(hTj@Yj397b`YpQuzy-aV>1={ zZWSob{I)XHukj-i3M=l*R80zjW7H6%C1c7|n0kNMKcM<7Q!!~rU>+8`GF1`;J71Kk zXtKPyEP1%f0MO4xuT14;wBfW~M;0ZiOhvsH>>9{aXEwFdB@%FCsuZE@zgIiM5NJu8 z7*jpwb+J0+Kc@OFrpkf>0Dmq3Jj4hUGFiw|YyJjVh z*74C?nNhDaJR}-3Y_&I;>Lqp`$TPn$A5H5tRz#x2tlvo zdO+BLYF5;3#FMGKumxp7rUH#GM2Y*cqL_+bS(a9xI-8hk1w5-t&0rf$<^0*OF0`=& zOf_hsI$|J6IeL$fd0O=Vrm76mEPW<}sS^IXZxKvWVaK)PBEU%`gomt!fizWQIV*C# zcOv&^PE#cYE$V8T%75jHyiP8f>V53a&2^C)PasHBp(JJma(P*3mZsVVN@{nYv=n^- zno9Hd_$(076wv2z>O?YzhPWo2Bm5CTLu3Qd#2&)%xMP}}W=p*5Qd8YsxLpVBM$}YK zQkq@RB5_jS&xia)xtK+0cpD*t3*QW$TIW&r~XtL4$0s>uVl za)_>YH(O$vQ-4A6^B#pu$ONCvsrp8txR!`4ftBmV{aN=JIx67b?V9>I)kiE(rf`W< z{Uy|>r^cz$Iog(iq20N$ajK>$Y*WI5E5l_WPW2l6ljAu&>#lE>ET<~*-yY52oa)@5 zgR1K<4wCO>i*8QUY6~&8(49_22n9=;RZ!45h^M06nt$(tNGUHQ=~Vp%7H1mOr!rX) z-1#^@)w%0xIK`)0p9(R#K2^>q<#b;tHUI(i-@(AA znrV9x-_)nk1 zLM}#$XMgz3LDseo!lVw2@~Qp>q!KRDVN1E#72_koTfMrRAG2SB5m|bJbD9FQU2gqm4@Ae!!S_QNz8Pq0O?2%ln)P7=^!;> zfhCh=Yq9K+YX~_cqb%*tE;jS>WHU$g(a6)l{jc9EE*z+WtLPqJBfMN-{U-udMOLxO zwg16rP0C|ZpsM~L^Wr!vloOP^%gTG8k$)pAxm3~i7VT4oqE28dP}K=)E2x45c^Oo> z=sB^%kg_0D5j$u43a0>^m`F6J0`{ihStWJ|s$PmamAY8T7eQ4JI8`0Mwj`*6%(CHJ zrdpf{svsD1>Jsw{(u4)7tP-4zAdtiaszUsWTovxn$u|lReohM&VRkG zyD!v_<(mCH5q3*^pvoG2N#N00BT%)^dm-7MCkAPOF)cmV30t9-%+9%3C!zzp7=fxk zXz#R+psIhjB3q!11XVbYTVyI_Oi%@k04)A?}pvo*UFD5qGY>@}z zh%fhvf~pd)J2BGqG?;Lo&yk6_iGO(4v?(+@s6q%hqC*vhuKFHOf`1Fb6F-M4 zNV|y+^-%eCr~>)axm-l@Cx@!CY1sL3G4rTFR29wnb9x`DjzOe~st}#fhM@}Lm)F@6 zy2((50Toe0sqZQ~zL|UkcB$V`WiTZT1Pgp}h{?*9AF-9}oJ==VDKFa#JHSqRsM=`>JLYD2P$x=B~7 zf{}~>X>_q-B}7^lw||O_5mkL3L)Gc

  1. sqs%xSblx&m_RbQ;=G5Uup8~uavsxF)n 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
    nF(P9F{8x} zEoMSKzD$lO$H8IPdY)%h>whq;zYYgE{NwgfM73-yhEcD(o2lG!B)N^FBxFX+^`d7L z?4H~Rs#^Bd^fuA4qxmi@0{YzM>94I_ zo-!!%srqT%9#!G4qp_mWqQs;G$E`V5(Q-(StB^jcyB@JEZ=SU{hWI=QhH=xJL9pF6 z-8@CE`==_}(MnFnl3JmZPN0?jr0EAuKdLH+;Z2SyTr3wRCV%~^hU|EYl}Sa4nB~u0V6}hB&9{Hg+qnJrSxGjfl~OaBL#~PYQTaPe1HQpF&HbaQ}`^3UA`pg z&tti5-LyFj+kdWeJum(|8uU+U3(ZXCa^D=3tSu0t)ms_PQ$wq{wxvauMd zo%XBf5i4kTU;(4G`=d4;!Qm9C&kXDo13LjBLuMxA75jM=C?u=iqnqj-lDbI7p67O3 z%WWiQLyIB}iup)>($wU_e_7_>H-BA7pBF%eiy0&%EPtIQ>FJn~FGC~+g95Fb{+ZV4 z1mtnua|k}4+!chLvG@He8i(P^aVf{Ko@Z{G8-6Ey9^rNT18Yv3yG#zd81&>YjKQ$w zxVto6_^smaC-|*<-kW8tmbqTHo2F`>eKbWkASZimfg*UIz!5;OXr*F4Jq&j3jzjr) zj?jY}?0;C%0wn}0zzh~Xz=Y7KK(L&)$FLU1*7HpD{Nh^Ws0xDNnlxSS+s`yN&wC_e z&jSJmixng!yJ^mtL`&3o!Q;hbhswx|NfcVwmg`*4dvQYI;(3p{rsQ}v$FG|1OH%f_ z5d_5bJegH1=J{ptJH0MRp0|KLt=vAvugS+p)qgZAuMMz651JSZ*6S`@Cb=rfU4K^1 z)ANMipZHzbbLoxW&a=sq7RTR2LnzvpCO($qhJ&AHK8_+3J;Ci4?26!a%AV(P49)Qm zFk-l{S|2xc0W0k=)1m|q8bs7^VFZpAJj9@pA=5Hb0u#w`j+UQReaUi+oZY+;mVe&pEJbPxYFkr%o9>(qLmW+Cy=O;LpCmn|Ig21oiZ0yVK}Kc< z42=orMR2?F!F`Y+sX!4sBoKs-8!aLv8vej*4w;zN{_&iwr$k5TYJ(xWX=jJ?fuKk? zP2IMY<65tqJx~Y1Jp}*sgz0w;k`ULf+kYro^6M^EBpy^mTBJlSSR|BBFM{1v9iwJh zA4h#0-|oXnF?H7_A5V*_WK4&iv>#r!{wY5Q%wUwKw`AG z%!H74l za_k}Fa-(GAM7z66;-`q0;~a*cwvB!Mxit9{HSzb<9LwQHb~a>Z1AiTBj#;qGW}3To zT7=nQT|Ip? zRk`kqC`4l2oh)U~gF9{pKiBI_v&^#X-!w&K>C)pbSS1+JKcdetTqhKe=ku~IuG9}M zTEwJMG_F+-nUonVAt);l%9E%3Jy+R0O<{cgQW~w~7!N_1+w3u{N6{w7S$`B!EhEA2 z$%Xr}#Ko}GvL!ga%hCnEJw<)?l|zvy$rX<$M9)vOT$<%|_&Kyv=6JGFu!;Y|ENwR!yVi z_$15sX@cT6CZ+n-)dZ_5N`HNd5s%`*M|!-4*Scx$ra6M;7ZfLOgpVIuz{Kzw^|C)| z!kwpXoPC}t=b;@=NG-c~^wqoa=16~?{B>rB^S3MuhTn#Bb~rUys2QtMN^Xlx(nE%^ zx0Rg@X>J?P1CReyPY@KTnC-CwGh&5G6FXcyfQK3{A|D35I0Q*TAb&kYO|d(p8r48~ zsZf!2azs0vRLk;`{9Cv6yq42M=2(SkG;=DwkU}JUj+3H?WtZ6D#|Y26ZkUJ}Dw+_Y z2aXmK8IvF8k7Nl0F~V?^+D;Q5>=C?0N3i zy&U&JO_Lr^@#v!!O*2lpK~YN4AyT~fNFh;S3c>0(w3>Wsp6(d~3rGMHKZa=0bPbc& z2bw^l2_JMUz=s<}#F%kWq3l!T*_MS!$cqya6dkwd7p2!t@_!nHmh~9M;n#NCh`)>6 z_w~BadKw|K;zPt`BUM_-Z9hd}FyzzKh2uF%_Gsy8(@*Te{yfIX%ZC*$K4Ml#aUA1! z7bCIQ=XdcqY_C)E`=@67aq6a#oJ}o)B0MjHA-Mq=Hy~%$p+T_@MHvk3aGXC-{yf0AiV_&gqq>jfhE7=J1zG+I*dv-GTK2!H=8nwMgK zmUj59-uC6g>n2HZHgCtlW*J7)q*Mi==Nx*wIeyd=HN!Q?tyqCNPyioF5CH=$(PKu6 zQz)d4^)XyKjYrMX9M7WYpC*Z2J?I&G+nRA`(9_Kk1+;^G0uRvq5);kzD1J}=9yPzG**yX6u6r)Eseej#M{a70TakYEsOidSXU19GGK?0= z)vK0s5k$8PaT2*efn1zCu(@ruEE~XyZ^I$YwF(L)kL0)k*-0@4ij;a? z&}o|V`sX}nuS>ByeV_nGyd1Y`o!%5*M1MUHGb%~sXfc5$emDZh3qCz>ll-`C9EPyh zrSP~4f43O@D!e_1-iP9ZiKbQc-Ey7V>5S5|^s*$nt@7_5^<*=Sju#UQFcAcg7?c>+ zDCL8%`1opeQS_UdT@Q+;SvJF@BxPj6O;ZLv8}vr_Tg9s610}|z~QQcMDcad zn%W}Rn`4ijs+PrHH-j>j+SlziP>ZtowE&REWGp`rLKX`hQ8XbXul$GSM-pU0p;zi&Ti_B#JSS^pZ3?AZ2G0 zid|D2f6x+U!zdwMCG@9&JTaz`N{&@7O9}ZnPGT-xDHr{&g!mWJ$27Nnd#(s!y)ZEm z!Eq2PRCY4J*xV3r=i`Xuete0h3POxL5RqT>@i_l%>=akbasA!v^wet(uGyT(z~ z?Kqa~x`cckCn7VYU&hN+V`QbOV~?Az2wAa={?iQmZEBNYEq-ms01X)*EU_9RDwNNzn`IU7+Q7_FC z{T{L9m;B#gvf}IOmuo5M-s(}BWA!@F=Fz1Ufgjfxs5{v)uUt;^MC18Zqr#b0Y(5Z z0}_Pb1H-C2hB19&kg#U3i0r^(CC7`2$fvi{F~)Ve;c|kPV=Z)dzX}+7ntVkvw{{-YAfQu9{RwNO8_S2K? zG#Tu4Q~cZ2^*%o>d$^1wAtOZ!5~xMXpH*!;&h@$xPoJ`F9W`A>9vHC{j1bk#s`MhJ zQjr>|?Ab0w4kQ2x87V6)98dXmnw3Z;)Q|YNX`bJ%`?Ttd%70|C?{?Gb5G=jzYNmZu z4YjSvZBtFNNzaAL#6#sI#|g=ZUdxm|F<>au{E~qD4KSgEjun=f)F_3D#H!^I+{$8z zW*Xah^hmlM2wK?`{FYo76~lHIzTRhcv@_dKjmvb<;q^R{o=?GWiB)w{w8gL%#g^+p zZ5wLc)?KIG7JrncpA==$bBR@1@e4xFlEa}t??tMVW2A+$-zu18$t}#tqw?WLt?FUb zHxy6K!_#%`I(5_XlDy0E^|+f9->#d(G2OWQL`OV2P0JM0-zs}H#jXi%SGL^Jaa}!2 z?5U!}!)Sp<@|eV)pVZt(ktau;BvUPOJMR6wI{fg^Lw~uds8w&1{Jd%kr+v4*M-j&> z)c_hQh?rT)al)!$vZ8sNNVCj#U5*zQA1qk>D>_4j6#*c8_#orL66)QrGq()}!PkzP zWI5!rG9!=;)rm$VTBRu~MMLPg?KYK^2?IVJNs`*kBaKEmfMo6gG;Kh1d@%MM3J zij)i(KmjUVDxq~H+3Oe6Z}Q(Hz8%|Vm63n)?>b6cE+8NEwx9H*MbJmhnjF!t>o^?K zu8(+rKSf=+&ARK9ER)!F_P}w&(R9$E9>tY?w_W$AsOfDlO+9pq}LtccNF}x<`>1IpXT<2^qY`Ge*2pmOO8`*J9}Q5W$@>nS!d*IJSqAj3V*eLJTF@o>GRwySI=8Ftwgt9ckHU$8X+bb z6v&m^eVVddr*d2@hQ4~LSe0JHSR%XqvSpO~wsQSn+n3{J5KPrIN@#SCB>Z`F2ks)7 zl4CzT`;$Dy?M;gN7|MM77C$%9Wfzd%BI;E(J&RxuGc6HF0*MqZmx-UB;1-tGEPs3c z*YpKDI`n+hgz-F%qJ2unjQk!Oy3c+ex zHP1Sl(xqqsA$ag01~Z@`K>@*SN^V=UNH0QAIaoq+sKj27tWG$Ly59r%;A7=<<0w{DHe8e7r%g+-%b#a|k}>$HUsrlvNY6)e9dccjkjG`ueL2!y_io%y znzHA?Ubk}G>wPh$5sJT~)>RkBa>FS(9k%PzjWfZo$^ZMP8gpAOiX<}~i+^R3s`h00 zZXDXlJZT!j?;loG(J;%1AMt6L7Kx=4BEheRWkoPdg5wwj&rI{zHPpHzmc>c(%(4#! zijrL~z=$LMcb37>h0iD%_M~V&j=!3oWEaGuCb(_U@~fUl@*6hKDwfs7(d9Oivk_68 zNH?uV(^2b^+V&p9niSox(|^%ZFH3wBRW2*Rs1Fv{NS~A7SJb-d#+8}IcHR4R`&puB zxCFBwHr-DX#IF5m#@?r!r-yBC^Sn_r#p|M19<}d@r%%OiK8CZ`xgFPXTt0?+8IIZ$ zhRY`X&7$QVHA_6atM^YdJ)=UcYWnoPAvF!DX@8O*bR4Z|ZlYtB(SKuT;_D~7u4-A6 zdYaah#IC$qz8$BVAnHLvE^~+ZNGoiq%ki>gULMn#+@)j7Bs`!tPin z#he^(l2kjd1KGSF7=K{70pUe)l&Yn49kFqI6w@s0PMkQ|I1nmm_Hul=t<^U93vtrq zPm~VD`}^9i6FHr81EPyz{edm#!J+tqUwtRZR6Lr9!YoO)+}?mjW^8~FqVswk=isv@(Y9tmKd-VYnDf=T8|+N zdg_+hq^PQC(=3;G{t;dO%hKF{m>ZCC15$24M6j!)tl8OUFiZh`VtNizRZoi6USD^ZOBzyI<2DJkc*Py^)tJAtE%c5q@Ewt z6sao7eMzo+hohQh_*l|mh=*NFpNGitC_O*sIEP&Rj-!N zlcYrtC9fs@{b|$MBu`B+Iv_F~t(er&HH=C6lpXhf89mD82#DBK) zSdN{{%rx9Q*{y3e?lsRWPNWk2F172Qnjv;=H|?{oH(FxUF7!@-sM@xh=jzr?@Jwo3 z5UYYHKzI|)ae`u^aWl(o@FTJ6NKIu(pBp2t6)P04s(VR}YL-JWnnoUa^Ltkl3E6W+1Rdw)RhiHdmQ)k?9)_7Q5XDJ&to~RHPfnk-p}$4 zy|m-n&$~-gf8H(iq3B)EM;Q0#Qd4(A1|C4*Z1>uQQ)k{kQz>T(^+ zZQo|Yp+J$Bq6aneaxA&*pQ8PFn&8(o$1Eq)$ww4o*_9H=B*P(A)y;C$HgVHJEfcej z$>E&v`bd70+7*>I>j&`Yro6PONl@AD}L~_~pZldt_v47k)Za{c6O(CVo z&NhN)rFe87DIzRZCKo5sil2|cAVRFb{Os{OQ$Rk&!>BL$5Y(C zar;Ux96Ot-Z8BNT*>0JARC5h*U=>De%~8{z1!-m%BN{wmN!X`9Zm;=BGoj|s=lFzr)J5f^|qbSv|d}5 zELoB~-M1>QPxTveR~H1|k+jjZF4373TQM}bZ9mgd3-2W9G0Pn-)30MckL9+H+lpQ} zMbocb|FEuIqJOEjky*#vY`o3JE=k-((~Y~y@n6?pO;xjuis2Fb%3^4nA^~>PSV^r+ zvJ7I`cr;he>!Uf3)p(Sq|x@Ld67?=Wt?S%r9~u#HA;nS zUe$|mxIV`-k7-nBWcrXwy2n)GzUtnj`FdZvap8vZfpF5T6FJ$o;jHAT5~W-uF(M@} zDpbq`Q-1-GUNUU>C;|y4P$AapFR7ebsQDJT?@p3%*Oi$jW*ez-=hm&9r<>=fZF@Mz z;aCL{#YR&8QVSJBp_zfP>mq$8kwPriDFjsarPyBCxg3HY2#OKhmVA29`|fFKnk7`Xu9S5eQ6g~%kw17lsrd!fAT-I3(u%~qwbBiGkNCeYLn=>c^t^5 z1%JVPU2F4vMG+=P7(Ln3tYx=F49v?2iO6Vl(semy-x6)d((FLj1MN|~(G+*%7t6&$ z1&kw*K!6BDaDpNb!3Z5N0SgpRfecK510HAr4Q7A>7{q{xBo5$$MU55c^pdFRh4V;y z(6rsSRMSTMo(2j@iOF;#f8fe8#yfCI>AL1Wa1;&V0W`$?(v!>^vIb6LXWn7gfubed6Pha?a%0THl3 z1``0m2{vE?7L>pRF`$7De&7KWz`%$CG%*AcOZ+fI5juSMh)JP>CB>zrhf@0IHh;G+5^7{5I@yR3!-pF@?6-!~Rz_ENG+~ik-8IU`KiM_OCVz>eqwD5x>2tf%=NWu+nV1ouMpaCyZtY~>aDZWSP z`TBk9D1PWUq(mY>5=T%#0~sJf6PVBhD_G$OPiVpxz~F^1j6n=hxB?ZD-~=R?;S6FJ zgBirI1ScfH1u;lL34c~lf(l^30ySpXC`qkgTCb8k=cPG=qT7A1_wA>tf<)v36|g`J zZZHEabRi2}5W^U7K!hPQK?+R>gBZB51~#Ap2~1D|3uN$u6u4joBS66kD3AdS?9jm@ zrUfP@MdkJRLq0w$zf(2+uWHNJf$)I?1ukF#30&X;B}BmqTYm_H7?x0lD5OCRXBYz+ z#E^t0D4_*1U_lUu00bG-00#?rfCW~(paE0GO8VcsF^FwhM4*?x_vzZJD~!mC2?8Ji zgBirY2~?oM6R?nlC}80VSLgy4mOuq4>>vm{XhIW=UfQ=hF zc# ziO3EXGY23*5igLz3t9jJ7dQb5N+?4a#^3`W0D%TLsDFVGj!*<6AmIoe5CI7s5CI4< zzyS?7022qGA>#%M1>^5NcjujbZL;)Hlmz65q)aSWum~cFBuEg07Z~9QPmsbDst^V; zh#?DJ_`(Ztz=H%#-~vS$AOSxVL4Y1QdWb>uqGd!SU*T39hxB~{3ZTRUDiDDez(56O zIKvp;@PCFjOo0kd=)n(g(1k8w0SZxQf(lrm0tY;xi61*`yquWCn3TA%fFTT9@IerCPy{3d z;R#Ss!VGR80}4<80t+~R0eHZnqs2-~%c=#6=YQHX*Y8V3aevk{qJbHxgU;(JW%tBUKC6o0A!LBf)$8B1u$Si2R^U?C6qv-28|glSg>fpf(1znM(RKH%989_K!!QYK@MaX!x)&LgdqR{2sT(k6NI1zEmWZgLg+yWR)2s3 z4S)ay23UXtd;lQ?OiYTF>2&IKmRHJ!O3BMAby5-$;0GQm2!RHGpo1BtAq{B&!ViRS z0~)}<3R7r;4}72l5}3dPFo*#Pe$ay(=l};bs6YiOkOUG&2+@NNK4{oDft8dzBaywk z^nWK!-y~m%uzI)wV+0}SfDUqC1Ai`bAq#0p!y2eS1u8s23Qowv7O)_NC_n)QGoXP4 zE}(%Sh(Kb89XrfW;esV*(z~)a03Fm$=Ty@&Okz7;P=w%-qXQeLfCHLX0tp~`6yS&; ze$=QTQYwAm*W-Q0OAPA3+RCzPgbmZe!IWwjcC2Y)hfg)2~D z3}Ofa8N}cPGK8TEGKhf-3ZR4$M-0(J4?lRgytM3KBZiDfh)5`;+IN$Yh*jGupZ}%f zzU#uFIFF;LjtLM14+SK^046ZO2ZZng8(@F}3uwXsNHhQeOI$z#3RnOG3h)6CRDc8| z&_EGC^cX>jOi-d0Brg>a$bXZjGYH-!X_Dg*{K_F{lcQdeCR%oJwQi(OR`0pf^&LfAMM-(hLxzeJh(H5D=z$H2AOs!YUZssv*a znUq4WP(MSWN`E1uTJiS^w^=u>y5&<&pN#%c{LUr0ew|3K1M#BLqa*~?s=7(`sEL~0 zR1L?tOup>WsEEqp*MHw?$%dRKUQ=GC9Whh{p@T-srcpfn3+Yp$->*5Y!LJs@*6TcK zx}zuCab}j4oldA#Q$9RNf6wKIhMQ}||Cm7oM2;Eznt$SDXo^*pklw;;9W7_^qeoFk z(NFvOEIg0r$jRQWBekmwkrl~)Yjup#Fr?3}(%-+Szm_97h2gQvC!bk~Qb?cNEJ?Ae zG6J=jNOCBSoQ}xh&>$E}QxwlG(&Kt-x=K5Z7cVp}C@+ye!eP~qi*&l5Th=G{>v(2Y z5H8m^iGR76LcWT=A_!)knG8d)8q;eZRz0=qVq%h!5iyZ=Pu=D%8NywbNPmg+lNJed z)1Mc05$QNeSTiaUO@jZ+df*rJI@j-?oKQN;&ZmgN@>8I^UL()Qc0V+G;v)5k5u*Sj zfGAm!QvQwNZ5XI``8&z3b(WP&vIV=4+m`q~EPu~SKtAbySD|~x3KSnYSmcnAB1R>a z5@K2rIjwLJQGxOr3Hkop#^uO@o=JX9uKPkN>16qG+^?phwpGP(N`6Ut{9jwMqrHBT z5juLHXxr^NHPhk`^i>uX%he0ZPKIvTNS6QhO~rH!Y2-2riRAc+jz{!MqTO9JSx!UE zuYU{3_n{&(M2i*`kso^7xg=dN6u~a(WqUGIy(~?Nlp|$ihnEu{Dj_HyoJYxiJ^9p> zL$Dsh61}>g1mfF|! zz9#s^&G9Zp5gnh1J|`W2LvQ4!H(;q5tkQ`-liaY#F6ZZnpJl(Qyqex5sh4F6j#obZ z$W4dX@`z1cPgCLt4kLd2fPFq8KM%tM<|78>hZZq9TuNGuuts*xiXsV@SENWG)qm7U zko2;xyGG%0lU!!KY^rfeIcx<992Rd6{nDj zCs)bt`)zqzvi2LMrJ8}UPBvbj=Sr_-T&7cQ_pSPY(^K8TJh%)j6~v4fAJ|3JFw34r z?dq%OU5dPF`YfuWv`S>xU&&j8^&GXzg889(K$N>#T@$7OPZ<-@L z|HAPdT~`nMHk&&woA_Y|8j_S+EKl!CYS|IHu4*}j^f>|Tq?c_sjiu))`+r6eiDFPA zl#fn&S^O;DF^or%^|+f1UARy}q*xh(#s$Yo?4-w2uX=h_*FA?^b|=f!`>Oc4iU(Ku z@TnQb&5|ZX5x@7-^d?7@4EtC)fe~Z1fcg~7l3KL{v#1(I8F@f3OPeDIc2V^lV*nT}5#st+6o15~EF{p2*op<~bmGbNvkW^OQ{z-Lt)C|RdAT{BX1FB3 zDI`#h5LYR7dDT?a@C%>UP!$NPmg%;6OyQZ-4QMSENhD) zXhlov>C)#hq@QG;Pk;RUqps@4d0KWvzfJbrH^+ZHd)Bm5aYgk3f`toHOrzx2b;~4} zrAJWZG&Ad*&WY0%({3Z1J&{CoV3>uT)<4I8*DJLi(kScCh zoJ2Dx(F_t84=Gw?RHptkPe>#J=-{EcZBdK1WVYqshlo+}fkkA-%LG&MW3hvYKuPG( z!OHpF6kSH18Gk4vF)I|!NmRrN7Z@)UUWzgOe&zK^c10`-t5;U-^63d;RTaP2O%g}N z7$-0mBQH;jmgaUur&DdZ;@MraQmx#+QSt<|$C!8?QeHJgRy9mwBtGAI9NKMk2)-Rh zkKiA-EFPzUsc@`>>_CCJlt3ogok59sw3vwG_IFPeQh!hK!A(Lu$)_K|@B4vhmfay} za#`IZeYEsbGeoa%kgOKaBSr#x)EF6!Zna!7g1D?r*KGob z9av7L*gfMw5wURsyMY4BaS|KZYnANorWn%SLVwV>gw%2fEq!xLxhm|AP5e5BNU8=( z2`P?A{?Cdi#4`HKq(&q<-2&Q6`dmNDe@#8z-qM9$MonAoti;uJ9HpHSL_pC{< zg?~v%5<765YNy1q^>J4^>{5_CLZq-(jQ9{5 zF0E)^iu2cgvCF0jOP+C5)pwNK$9#Ucih#@o6O4X%E6Jw2peo&i=*7M}5 zDVR06s%nOR^h7(GlhCfj4j#K%-X!?DsecQPU3aXq;gKItA$>@*yTVnHLBcW#?J-DH zG)`VHUDwZ(1ji*l@4D%{6iq_E2Hk|5oMkBhy{1gk7!rXy;ssIgLlVgmj0%ZA>KmLqvEkZCcw z>go4Vo^M!z9AEn-ns+Vu&St{21Zy6_5@iM)JWDOOg7#h?!QvQ2u%naYMxcc<7PhwSx8a zi^;=6MVj$S!EmWkx$T17>5gf*q|lJSUYvx$5NXAvMxh#hPjS`fHcCuHs;Z*o>li`( zXc2i4Q>lFT7jD~TiGQ1;XH9c+JjJXKDNtm%NGw<&6|IqKhDG?j0}4Pu0ebX!(KCuo zQT+RbYeXY5h{U7&Xel%DmuTTK zQ~JPwauuGA=`_!#F3_ilA3k=pXi?(tJEXvVNO|E{l}73~q<@+s_B6pOj}#~xZ~y{w z(5!$-nKZBfB@*z1jvp~7FJPz{m&s)GIn^=`Fk0-uqQwoBkq8j+qX!z78Pi8aN)*#; zenn{#Gy(M~ArH!DPk{=_oI)adO~TVXR77s1h?tN-BpUtz0+f&;B7%C^nEm#EAt_?U zj1MhdUQnWvkAH_xO;MY+{GO7_viNq2-}Z5$0z!pKi&kkz$;yY77ztKyxS({P2_|Ha zgpBz3NN=-@JTIqEtj9rdyR#_{m5>yp4+$MhDT#8`mL0S1c?7d9d^Yj$GAB?IJ614Z zLxsyJG{WB^d#w`UNq*f#yEbHAc4)zZM+!^=Jjg)64S$*+Eg~vXq7+Yl;^VA)PSJ1= znHLx*DJn+41$LM*fgye%A%g}+2x`Ua1Y^Y2x@Q(5sT6oX12tM$XrR25xWU4Q%!!Ma z$pzHM>KOL4ky2lR3ozgSJou2}$%Rm%5(G`k4wTjpF(pU5aM|%2^ z|D@)P5fl!Rl?#|D#R-(+>#dvSS=C?B81(4TTOoN=v_>!@UB$^q+3AltvXnq9L|!Mm zhT-wc=vVpZDgRxv%cnNQN3bqMnOF;kl|ljaDSuEP7_HC?Rmg{y5h7$vWJ3G9WhLk?Dv`RHpLR3zon4izMN~St~S&>?hv`l`S1*a^eeg*9G za(@c3YB+^TOqbx$G*pVNfC#G1W>5q_X9dUyt2#44+-ZR4OBn$wzlN zeMo+sLO3_n(zdHJunLz=aJSaM1%^+Y(nwT;30|oXH;!k=T zq?)`bu7o_UyH>I1t2Ifgh?3jUihr71HMY}t35s~~7$vWkQYa*+Q~X;br&YELdQlZo zzp~>Hyq5es3s)*eOGRSKB|08b&rfRpX!)b1N{&}FO@m?FeJ(my@$4nJEjmTE1M;Cj z(V^I+S=NuC{dq3e!QU5~Uz(FBB)9XDe7P;=xIGL{`uso5&uN}qHpR1(*njk@Wku+T zlVS{ZDN3M|-Ts^-2@bRT`3Nq%3OyZy$?Eq(2~TSQF2@4uIgdU;VU@iOncbG$mfSS{IDefKOMJct%1J(sA=PXN@h?~<72hto;Zu8h*wgc>E~8)N z??GsJa@min?q!LC9u01`S=uF8g53~%eo&H>mLsyOV^!J_qM9jv`cquF&MU8(xbZ>< z3rj4gOZb^&r%^EbqT>+VPSr7}Ow)@iOBWk>GA&p>2pcbrCjF*bVs)X|MEnCJ*vR6+L zoKC$fC_PK=N^;98LcI(Vs6@AQQryk)DR|B6Xus$-GeR{8f9k)IOsK`knJ(_JN3l)RYWp}d_{ zR2$*HwUGe9-HHVW9<;c-ySsaFD<0h4ODRyixI=L*5-0@KQYbex@%ygcFNBa#zUq^T*^Si)IpXyUylNBHN!g)u*NjTp z+qPbSuXP`NZ_;#nj}k)V>JlP1%u|>Gpo?lPH|<}@&op?-@*9_vr|U8Gl|~bYGM1=+ z+Ae#+`u05vq&#=qnf>dUT~~P5E`p>qILB}UpKPkU=DG6BzB-L#k3E<~joHCv62EnY z>8#ZZ(*^Gw*2mtl#?#ic8XSBX6r3#ltRjU$wj(u*_byu#TGt=vPG2Ba0=+VcQ1t3T zwZCE&H{FKO_C3mAkx%*SxrLR^#wcudS3%OX81tvZb7LbjdQB^p_U;*vWF?MI(d)E= zYggw?0cysx5@K?-lhqDTJv;oL(*(E8tisr(H^Vpg7BL)4TKFnTl0X^(d25D1c5O9H z;~dLu@JQ~?62Afkv3qS=+C#4jv|mq9EpSQqHRg}*H;f5f5)Ro`mkc8^tr_f%s4+?j zr+`GzYd{;>(NRLFi)*t3X@)Ur#(8$#_2E$gb)3M0lsLGJe!R2g6(8k!6-)V+zd*Dzk*gJQZ>EtZgT|8%R;DtC4zA)%8J&#ODv9)Gm;m_!e5+HzLCDFwa;7ikmkjSIPWBMa3id% z0=ktxJY(v!F0@~Ko241gcUV%rw)>E{iN+`6Cu+CUzcN>R;I@_kgmdh-^F3p;G@~R` z4Vs_y+j~oyVXpKJ-kICb)Iw-N{-0kh)dGQMbt2ZP8MaM$@0y zt~N~#2wtj>)Ne!We{-`pGd$Z0o)=cSt33R;H{omY_El;57p9at)2*~w$fM4}gw{wk zy*^(P3x^-)pTf@(*7@(|R_g7A%9jlOirK?1my(UpP`Yz7GBxU!j(+#MVL7*2M@O7? zjk!AU`9_Pr)l^gugo>lwECIFBPlR9rMGaUcUS1R4yt9t?#1z$g}1hcQ)`^Y?; zH@!aF&LqwINPb3_M9aOFuRSiR0uCv4;A)sX2d@*{ITY>Yit{N_+9?;3294S z_E$N7CjO&w_bQExf?-0WAsT|!FLMGbJ>Ji@Yf2iNBc5*}gzjg)8vSbN`}*O#&0&nq z?~sGVI{Ji!s#*8in;M^_4FgffAFL3; z8RQP1v|Wi;`=LJ{p=%#)%alKR4W=nGb+~?I-*M4)dp7I9mP;At9&Wie`wNugtV^?o zNNc}v^+t%~l+e+&(AhEGTCH*XNntro<`BGM zHPPl-aA3_n46c_j%`BF7HnOdzo`3!ESEWzq%rpY(p7n1`vUmyCdN%-F%rzs)$OmWo zYrgA2#kRBQOZ3IzWS!f5neh9;KJ@DHv3M-7EQiPdeV%FA)8o`JN3umwmM^-zKP6Q4NXmA5& znupb(*26c&aUO@H*-@hGxwe}cHw$+yT>?&f9_$$|LXm!CG}e+H8|4Vhc~%Hm#)_7x z;-Za_RML&e4wZrg&4Jj`@FVlGfqMb%n9zN|DY&nWpTaW`Rv}|EI zcDq7MkDoI&zc7?}aW}16&fee+(Z&5t#MMxRMxO-Cz8?M2t2?f&IvteY!utZAsrW$M zR_mT0Wb_0{T8GwVUJ*_FZfnH(veX`Ru5|M$zp?X)uO=E@o-hD|F_;{i*=P#@K@$we zC3pcL%8pCpkbn^P5Fv55T!<2*3Qo}Iv2$bm{hDrvWZ8P$-jB{^fO*`;!Ni3*+hkQRzWN2=UO zC#a}}^+x+q&b0MbF()8>KGDErJ^B4A?x!CEWMRl`bj3)th6qtOvV)jdTtmdbMr0;c zOLUXYFv332{0O?Cxaz>pFk&+dZJL4$byiqLR8R?8>|ND@sGuIejaAfhVfT_RGC^ID=*ldJq-{Xp#S zHGuWrz~^269SzFTxnZBLM6UEj21zN2LaT>pm~AYuiYp9gsks4%Xfz|imL5?Q)V~lR zKr<-p#uXbDCT&P9C@1nBTytRF;+Ml8QCC^l9zTAU}(7&jwG54*kI2X z(DSP*CcI0%WW*0%{3jqj!7Ez$#N0lYj(~?t#^ouA8QK_5h+!GUeq)KuGC(GUAM7E8 zpN|(ogoOZLzCb{x`++Bg%yXTY1g#b)>Z$H)A7vHdfq0F1mV-E~ibKdL#VFLwz=%YwAD}o=qD>@~ zg9uy}1ZgNHX3u~es{O?#QPAA>Aa_km^1JUjsx-YXw+MA23It zNZ^GN^%RFuOVT33F(>q@i)b2sgeW3HOQ|HBp9AIbcx8T;VKk8lp;(NoL^PtkwkK+P znJQS%ZWDzYRdy@y`$7}2sA!7^0VN1AAp4CdAQXg%DZ-3QAV}^?d&3n3MdZ8 zC)q$|DvKMyumt@;qM$oKoJ3S^oCjJB>jx{F)~f5*?jENwW8~22#IeNSaSMAOQ$n~R z67diazo7+Mj^I=@M&UJHY@;Z3(ne4+TLN{`5Qs(wB2kq6!x6AFrF}pn!zfo|m>vo& zP`BRlrD7jln1-ps+Zgw!_g?{*VIb=?0F7fKfX+EB3WNDGCZ*#L8QuUg+W{UTuCb~m ziioHtGMz30;0>?`K<;Bx!z$O811a8ZnZ$_YMQYpolwgT;;!z^^T~f-4 z6hE)cwU6eMAY_zADST_)ViXP41o;nnHQNpAtc1;`0;u03vMh4|8FmkRJZeCYg*ZD~Ak9j4X|aHy(x^+&GM)ihvxC zvf)^Q#FKB)0DD!Q`Y@{jyp`1Hfu(uNFXTrnbpWjK%wFT5NbFOE_RkqF6B zk`a-XdDqd}{_!$n1IiUdh~GQgT(|sSzuEjO?yefJ5Qj=2Bz%E{4Gzb^{4q#qi~}EE zg2BbwBgoQ$5d3cLsEJYBc}$ura>)a3kvvF#+Rumt!>Pxe^vlI`E7EUA&yU5b_KpXZ zlBijlQaC(m00@y3GE*McB_av-#Q-j)VK@$YRPoLjK?8*xw8w`GWHF)jL`!{4zFA4I7|0?B)HS1+r~JdvZDqp%oC<~r zax&-|SgE04d5h0GHCbdk+Uvn`+kweyEz_kO&*{$oh2WJSWe(yGq~ai{QUv@BL`2*X z%^^hAD6RoTxfr!K_@ldJ)t=vLhRdOQT+cFH)~DD>~-GtB0%PD?R$u7gchBiV}Nb%dkW>;n{gT zqUJEgPlTm&!%H46wt64<*Q&xj+w$y958&jFk|ds zx$U=R%Oy{ZQ!!2Wbgy8#fBsgF`xA_0x6f@@N(>J$Pjl<~wSjmRceDuckUJYTZ@>15=IcNeViXc}n)WbS z28C3ap<}Qqcc@cTVE`U){{>)>hjE_D_Z>mDR0hfPi|w&j$gmFMY>w=H9%K9m{OIBW zx8!#3uJ7$@^H;t5{ePv}INXLi_On%>jkT4hE)_j&`dl^+DBRTFlID_T>W^C-S1|>GsCeh4kDXIXVv$D2Cw`|D%;?GM zQOC$AS85g(MfDgm5>HQ--?dpDSXtMl$>R-R~ zN)yqfY1~sa{(0L@KK1WTwFE=vr~V&1eV)mu)IKHdsY(?$wd;>eG4A=v{i>L4uB-gH z>GDjQ-e-&Tz%bmu!4v`bS7((g(Sj>9oY#J&laauRM{odt$GUPtRVAxs^iyZ)`Qcg6U5MDl&#tu0%Nmh{Y5Z0Flr^llm)>6jq0pY!>uYQ{q$m38yB=D*& zkK%9JKzqvN*&%g)x7Gc66;sITo=oMEg!AUTMa0h1vUnZapcl4Uq3E%Ok3)J(lgXT5JygPWwKSfX}v>>=(g{g@Lk8>hcW_3q9Vtm5WaxW z#SR>$j_VP1g;&w6@2QTW3I^SxcIjnG-WnR@ITKm?98O!+6eX9GW{=lDVLTEuG%<45 zE+3iDnpxVCNJdS;3eKy%>ky}s((w$d+7i&rzajpIWHzTO&XcQPw*{!(s5isRFIhPs z^$xF~`asB7+1G~JN?$FWwG8RcqMtwY-5M?~FTM`kBU^Am+FZq9)I7!)#ie5$>kKnA zL$?y`7PBw>1>X^T8r9NK!JPc##x<;3=AvB~@(l$N>hdSO@7<%pmc zHoqpUfkx!`ZJKVm2BKcVRH&i&ugKUSlews`#c|GhqQUJYne9Hn^Z|`hSethK^c($~ z`{iGZGrrfdc&9S6_yQ)AA=P)DOp@lBNpcyt1A$-#57UhOF4;pUu{`YdSo){zyS8!z zLwU1hT{F#EwV%-KH+o~=7bED*O4>d|SexL?+)>P*Z7<~jDFrQC#|tQdZi1{!;nt5? z$0hgEGejg4Tr0mcf+e&_hN;A4Jc9SGhpqkPV6=fa&MWy~Un0KgR?IOm<~L9a(HyS$ z?@T6so1MP3f^+*&;mm_B&({6jl7ogCrj*zFkU9CcJYf|Jf@={uw(gFf~ZE&v&HoFA&nkY zwi?vzQsC6dy*}`)h0PPIkPt0#_UQ2W9+y29juGID`L6gwiBTq663%UC=YlJWzEZQ4 z8=HfR@=_GIeP$|W8iubN?@#?=X7zsxYi*Aw>wLxfhMpvA+cn>&VKD!@uh=kX0Jib}wX0|+GdJdbp-tH>Q9@SYS z(zv8B0%!jD#-iA~9Z+-nZ?cdSXO?%p@94f)nubUZo3u$a?RsRKs>qmVp)_|x^O z5>Hu0^&)#6+eyea9Cr%~-uqqYNkpkq*CK=OTf*&Ur$tvDB^grv-HPnm-@I2rLMsbW z^w6UW9iP|67;V(#fDrdJ|2_T|nhMNu%_tcDL9MU`Ic*B=*F+ujxn9H_nb+4aSyOeN z`#JB!pA!K9x{!-TLtb?b4sODsRZTwte*<26)kt4oDYKx;ia+DI#MN*! z4_V)OWocD>y^i3Y7Pk6&h*Q@di#BWcP8Z79j7yPU>cls>mZZt^KfVt0vQqK7+Kh%o zaP-zHhUYe>kUr)ZN?!fb5xjnnD-1QxI?_8WF>(GO%umSDd(^fhUeH9JNb z_Eal}{uv)*18GeQX~{T78({VKbI_g%{Jq^~+PBx~?szBPqdIM?vO39}vZF$O9Pcr8 z)x3j%uLfG4<9*293?h}s9N(1eFFz}$C9i}KJ4zBV!KpaafFdM0=O)%B+dHQI4=dlX zo`PH!$8 z>H0I0&1Z}z%V_bF-_j^3<+o4or~qS4l&SdP`l8}adhS}Le5b?pEZyH#G7a>I8%p6q z=e~jR=bSlUSu=#?C$4}?RGy(2tR3X^h(Irwl1ofbRHT(|sZ1*hQ1E$@1z?Q&i01n^ zomIby*wl5+v|Qd5GzKW~LviNBK%dZ-{aE^ylFW+c$m8+}giNVtRH~QrY87+xfQ!~$N$X5^bjH8M{(65g3@81NwLjU}V z+6D#OEw_PhQj_HA`p<3TrttX9fB8#UJqpFAge=KPHKwi;5U||ELVdD&$9R4YeR%^t z6jq&-&8#>MXL++fbHk{#2HsCynQr1&dKT4?ro&Ar*)fo}6CS-{&+uBBExNB{f#$QD zRjLu;^pkw~fy=|7@FUNvL;;HuLJO&yRbFz?rfhTmVi%MN>29vsYjJ4fD+wj>f*n0FzeP=vw)%SR7K~itSZB7)ljjUW z*x|ROQ2JXyol`=N&2w+_J}C>Mp!UE86YKRojTf(0@dawJ%zGrcSU51AgGN>Y1i2hI zwoRYi~EBvp*)h)ukNZ)Of&!VC`d!`eb&aUgwhF+3g!wV)(Ea04G1 z4SlM7rCsUwcBk}Cb_A9rNDd;qce9B=C~6yqAP$26^lA#|`Eg#oePbPZjR zR??&oA}uudWI&r|+s^uO=d5C_%zq^14K*ANQ>WHQ8fQbN;+0K9pvOUov{Q&bimqub zS)=nA_m?J!Dn?+$L&!fTo>Ksl@u*y+eU8E=;gvPzrkhcT5j*KBl~3;ZzBG4Zzzgoz z2u)RSv>mz+Qkopmm;8}8`;_;Gr)~D<_7JO?0XH;0)c=wIks&N>aEJ$4XU%YvK<3KX zDqU7S7*_hROqHF9mKVar-h`+9J@zQDEK2*85x4I69jxICMFfvh;OhHRVIwRt=gA@0 ztXZbTb1>Q2^xP>g5q6^g!;7q#Q^M9Ur+ztcmG--se>rw?m-NHD@}_i!V%akcAb80R z>kf1~_v9&dnS`E}L4dKYF5dSL&s4ze#MUOK_w&KgKfSY>g8c}~|Cm49rLOYvb=E+h z*)QT|862t9(r?n;R@#=>^}(bN!X&tUzT2y}c5yv$cJRlSn`rqQ-$DWXk{zru(Qv_f#!b`&n9!fGC3sVqQmjKO$so|j>!CqCmpluOztRUDaa z$o1Ro;il6bqIAX4YpKCTxC7{EMeb+jL-)R`PT$#A_o5sl^-@xu!;$&Yo50CR8uVBC zAP(+yeVRE1IVbCyG^hq=g30{LU)HY7O_)=4PMhhz-=A1tC$6l)hF!sqYk%Hexh4gz z)UL@t4sL>hN;#GW4E+$}l-r<#5EIv$!5`)O#qHP=KFk$r+@cvve3q=)sp|O_FW~!3 zpF_bFUw@~*BV#6&2V~!CpS9L5S8h%cytjsVXF}AYI_%v~qR{AhmLTc{o_}3UMq6x0 zW9**_s|!drT{9#)n&{{N$(YW5-kGWk-if3zK)>hMX{mqYI!8(X+20!Dq3Zx5r3aJU zb*<9`Wi4X*R6K4L5p8+%PRi#877dLq2h@u+WVA^|NP`6A&DNlfVj;e{#xlR!4c1K_XLH-~N^{q$x70cp0DC~+|B!7Pq*=okSyY9q85)Uuu zc!TprJuLXQK6or(@~G;_%M93zujQhOhr^M>vDCDR>b#UsDR)LOf3I^KqkGFcL#@Z~ zC{b;aJJ@hj&r$jhRRtXvVr`n#dY4%?c6AHwdqh6x$`bRy8Ko9e5e-6*dDShK^Ewp` z1pm`p)0uy7MAq=Hkq%V>a}szUu-ma#f9`a3BAmJX+x5jL=H!vJL*@cA&@AnEt75sR zD$dnr#-ybdN9ITMUIlu>E;&GteON8)XBW#-$h14Gf2>xWgSIJcu4-Xl$kI?-$SoHN zR@5`|6B}Bm^*>db<$ZCxv7PnSNZ=HapJ-Inn*Z6DOC0_DQP%`m4kM{C4(exe-eh&qBRu3 z{rZ=sB-jgwHw>MOkyffFEnN~{6oelsFR$7r&DSM0u) z0iBx>NU}Ew)+nkIWB=CLUnTFJWt&bq+|BaYmZr-OJ2=r(G`z5!4RTXvG@(-QkA(!Q zDHr@I=zvnzUQ3qc@R$TTw?;#Jq@Y_7cv0yVxC~WADVZq!gCWe^C@Ump^vo@zV#RN^ zd8oU2g6Rh7Ded5)sKjF_8C0OVZH*U!Tg9y8)Sm`GJew=~H)5bm;orXKq z1Mzy!S9@MntYOIOwD#C@c@FZ-zS6)7Eo|J6|Csz7!c3Qrl_+;@4UIM@{VT3F|GWO} z^i6U#w^G+b@2>6D=BO$lOHdj~g-nY6DsW5-TS`<-P850)E~1oj6Ns6cAPeIcD*5z1 z4k=nWMds!2`!yk~*K4fJdmYN62Vpp384K#G5I3y#aB4Dt3Aq|{h${ec$yA^aYYV+9 z9Qe}`Ahr3J$iZ}np*ssQSP2BY_E~A%r4hbc>$gyowCBZA|J+h^KfR{o>RtV*m;+Ch zTs3Y?o2L<<$DIml0-uR0tR-N1huWD^#!?}>p5kl563a+~-W|otA*uE`ojsgPn8WwJ zLXN@s0@OQt;StHnlAATOPFdx;L4r!l;h`;Y-98Q)Azka_7nRph^Umsh)P6S zB~HYe=X6gd_rdvAG-BQvE??_VR8fY@MmW@XVfq&});Cs%w;})?oG}s!YTI$mL7;4L z%{%uDh;}h1(I7oC;NdfngoDGlvKMU7pduYdL*N0GB`7AX;2!!NPGFKw_u<0-i{7=C zZCfqM1Q00#BrUPfw!L@Z{rJFXT0_lDC#o(l3hBP`=e-C+-O;2SZYkld%fRKZwwE?! zdv!bHSM88E7KW!dc@+$eYQyIe88<3CxWgh?rQJ7?8y5rZ)tkoGw#F?394SS^WkcD9^l~m%4 ztXZ0bb$%>QqL53GII~N9NH2T5o(AMdNKSVu#*_qAYz?e*EbIm2E)I$AFcKG0Dk`;K zik|OEIIBJ^iGG75AFY61D_6rCSr$DUEemA?M`yt0b_7YH@W_}+mJ#cx+A|@&F^QT! z5OXSBLp8N8yU-_(7Nd^j))IDVQ(0M>P z6Pw?2dX`AznqcPqvd@pf*B&B`e}2v0!%`w|s(l+2e{bbQSY??pwXxx{X;(=i6L2Bm zib#u!kB0>D>22~(RqG<7EFyq>PQsLgHu+WGjn4UsiK+34CaldK*PjT*0Bf>W8uoX$ z-2f#O4G zsbs(6zUbw~(MBQx$p(;=0GGp~wxh~BJVz>xHEt$YB*Z9WQUKV*PA>f+lvk+&oldFi z_+Tc;s4n40(=h*h+qN5i@B(*yQDvSai#c-+zN3K_6{mgFuXOr5^xg+ixntg8p08Qb zoT}1}#c@9d^b7B!Zv!zCh^~#>iWRx3aYq|4Lp_0iIVfU_u>9(K^tA{k9=a8&iY)bU zr0%ah=6e*paRz_Be9M!SfEsGLWkq$`2XIaZU|nCUPV+dYNVz<9ZIaSe(^yY|;Qmd8 zgE%Yrm&rw2N1Q#*!1rWwJ6zJOG*n#a_WRiP>i)wgb(kl0hU(|t++Mdq*50sk!?#H# z7DzkA;RrM$RbjHs66!KLy0Y-v5Ze(veEPlEDfC3c>d|!`O?g9YD40ln$!7d&RxMCO zwn)KWL~gkDB}M78rN3+QdPz&ISSn!CN4OX$K$$WB1?K6y#0(r4&*3y7-3-yfJfgGzmIjE<|NLvx;TcxV_|ygr-;$ zPakDT8?RY+L@x7j#SjPBjhNnhraMndi$Ce=FI4jIoVlNooicpZkYNq^vG8YxAKUL8GcK>p^8opZ>D^ZDMGin#5#j89kW ziVB1GIfFac@A&)Wltcxml>jhCChBYaB7kn&1k7JU=CC32LjUh#h zL#^PG18#j?MOj)hPW?oOe;RYBV*JPB=1j30q6x1@lQ!tiD|oQKPLr8smEiYUBcqz% zCgK1Gcx{TE`+i3`h~qDWCG4>?_GqXQ}TyPekt7DZsDTkQ@y3o5^<>(3*&! z^AZ#7E8{porEw-{RIDDbPH=CGB4qdapMz88)^E>39N}Mz_~e7AylHb7PHR4iz@Ny| zs;Rj-BtMD*&1mGjTVAAMFu}nmn$_2<4zJ<84LWMiZcj3a*$h78 zg3_@`)GYdj>cu)@*R@aGb@dfZ|5{28@u`^VoKHh;nDRjsd@#SIQKh-jZJ{=fYEKTq zqy7Ak)hp4jU;lt7{fZOsb*i-Kd;b~1vu9Hl^=Om=!JdTJZD;2BqR*4X&?-Dhv}6Xa zABrX4Fu~4^58ccJ{>qrDi(TcxWR)z+}G}rkJE2-ZsnF( z`#CdN%Y#Kr{cdYnuy%}S3ASgh4B*-k4t>lPVnvi~hlRS01a9;?rsge;08K$AiQAB$f+ZYo%ZyGU`v1}bVCBdPSs_?b)FnEUfk$uw> zS9z}3!AtVMoucV56&^nt;$`-DeDdSiz(+zQ1j{CnN+epoTaD2B(?g-Y+VCBv+%Rhq z_2~TA#T$9mZ0)D09sber$f#Lrxr`?PHSO^Q zI31fl>IELRZRl^{{->P3V^;pDprlZ}ziZJq%T3Zek>H9FJ2F;AhRy+i{lHXfl`1bp zTV(=w^Bv)9;O|>(DGW->_zYZNYqLneTwztN<7&W;ed(bG-2(qS#eUgDhwH@RIZxfh z{H-+K@l;BCpvCz4?5tNt*Jb?b~;{YnpFRRf6Jm-2# ze*2#^8w$zvfj|A5thYSv-~lSK^!n#UBJK-a#^7h&)7duaiL7JYnMluGeeF7^or=fv zZ_gjk6(_$(kJ*cuCa&TO-?cW5D@x(FvDSo|++rHf8e+K#rBO*L8;wJr?K z{v9wQa>uKM4qlAjmjvEfIIL7B&X`W;_3y!pu?)}o3>7mr( zVB=Av0;qhC+3kcMrVaV~!hKOI-cKdR3~g&4%hghnKqwqXen0G2|?C3%OMh@MCQ_XHbEUp7>f2U0JzV$7_3SK+I> z?YlvSO1VA@Qb~vf6>D93Zg)|taKpBaS^s_Kd9u(hHE=7n{}o5Yn@}7{(Wp1$Lgr3FW=W5emWJio zxnvzNO$7rByW^&U;YUVmp2XKf@6Xlb9nl zwzV4ai*MOi&$Zto&e;UmJOLxfs_u0<+BSsJbUR(_FYG9KnP_xXQWLM8YPlkA4beF)d>V{zZ!f53mGM392j8Ka2kBP)le1VBU!{ zC=Af44~!UV$TQI7P;@@}SI)C%|xU+6Dpvf(5O)um|ek(to_X6_l zjS}SyVr;9Ku!A#BbXRXZ5WsINqwx8(6+x1KR0jX?G^=d+Mv>IAFHWJ(Lvhev^%ku8 zTLQ}KgM7D7otq=;u|>?3G1XKCLLZcQC?hc*)1(eRqo~3^Jqecqd~y``LyCUkn9a0` zge}uKRI2WcmyXWK*3PfW1G9X7p=vV)Kc0M|LvC9Ujo0}ttj-?-EA@VeOec_a7+iR=XCkE< z>~v6Hvp8N6v0ahpB+=R!f5;o(N#njuBTskIqW`9df=N!!M$lnEPt40}Z#u;_n0u0j zObNx*DaVpzj7^HS|Hh(*A~}?Rr$hLE_=Ns%?@Z_a|A7AYmkIy-rvJSy|BwCu&+RPI z{Zn@DH$)ZkVw9<=aho7YML8Wwu`gs(?7R;m)FuS?Sn^todrWLRmfR1$K_eRyv zw@Q^w^igTGWR3?Qg_yb>=NJW7oGCS&FuYBO`~Dz~?m91L4||Gmc4nkZKixP9JF$<(GJJGQ$Cst(oE#7JbBq3dQ!7xCTj!R z3ZcT}?FEe1^SLKE>O8VBFXQ3Aw9E2~bZ!Q%k>vwGygywOe)dTw_?@KeNX3Yeii= zFf}iW6x{GDw7-xsk@}0C$~)!vZwg#pG;~H!5AAHa#3SswDOJ2rx(|Jf)@gy9_kNwVnHGWxRUsuS_PfKEOTx&S)cz(uZyB?-i@X z7iIgbZVE2sr2X0RUIgGaW@f%qtza2kg;Bgug;oT!{&(|%bJR@lSH>vse?$sllMd!g z4aLF3U1W8AM9V6u^+qQ=M=F{+^**c<T}jZsU2BDxU9uQ^Es4pR z(qF->wQ{<*$_|FkXR7N>NW7P+{aE2vvlAt;0Ie($PI%j@#eXPQsI9<1LFyY(8-HsJ zl4^XFl>4ij##5Cs&w>$n_EFkHhk1-3dg!7KL-!?oMBB1cX!EiftC)COU)YK@adA+g z!V{cp%O24Itl!|b>#(_{P=*Bqyf0OH-_@iXHzamop^0fZ-`SF_IFU0pXuLd~IL+OQ z8ck9ey#F-idQmX}H5Z7mTH4-dxUQ`U&1djEXE{3^u;2JVn~qcAy7nIiA?+oz7oXY8 zuP&tBS&4y@7dX9b^PdOP_C_%d@7H*xn2n8>@TlBB6HLSfl3~aG>+m$I?f_`tz@XTjrHe^Kr1s*;m^cx!^w>N^fBvS|>_Ptre{NqY^mx!Y|i`o+8)M z@s{lpV@s4DH~gyg zBD&I0drjJflez8t7S=;wwMID-rQ>m{2U5tcpc11XK4EA9SXB>&_uJu|xZ%78%y z-M;X1Ra%AgBwzQZOw8T>E}cKrj5CnsPPNwwU1KaRFuMS!98Bd`{q0cc@~(4r`-DAm z)D?>*ivT12q0c>^qjH*NDc5>}J$9hZ0k8{ISNRG-PaTf?qT6~>RCyF)CrM3XsOU4I zPW$#*YGNk;uS-yD`PbzhqnscrB{>scc+oH4w&IWl{nZH`c zDL*dyEbz3zF)2U1j-EG?Rh#D~SP@sv!4BTfo{L9xk;Q03 zd0V^wpcCz(?)>c`>TcfY_z8E~@9hW%QFErW?3Yue^te`LtF=K?W8|F4{bpE9_U3*5 z_UXZl*;mFIQN`wr>gQTUVq`=gaa%s;inY~_LGHCiq;TzW)AN+l;X_t?L#DB|?KAD0 zGR7}v8mGdnawLiMT+zBOe-+e3Z!A5bH8!?(@Df2Jl)z1b%(`1-6!8*&eP@p4jei48`|QMSq)kixmW>URdKy5FLBw_cTG{1CQSJL%t!0#J zr_e!Cd}<6?S|hu~Y4j|iA@xTyZhzN`*AK!kl@oRn`|***AAN&cUbNog$`ApfYwX;{ zdd(G5t*JtfbH0!>(e#q*#-a3ISJZv_4n!LISpHuCB|+N0f0}Hjot%!_ak@#SW>|m! zzF6DPP6y38@V3QW$7wpO}(}KTB5YjNW%- z?^$mvLP>uFL*`fJ@z5ig=MtLkVt9jK z?R7T@!d!Q9I5WqsS;lJFkDjS^ZNaW-RsR&#NzvSZoE(rD^j0lg#!E)`7Kc55Oavl7EskVtAAh#HX)HH;aA#w{c$NBT%&&y5HbDHk9 znH`SjBx|fnHC7}MJNC_y4!vGI{S~!GkT*pTN~&Jf1xp0ObrQ*El>Dw8SC8NdZbN9e zC9|bB{jex*l6|V`t1;g|HJbkmo$q>Y*qgC}w@<+`){CHCQPm+Hp!#^$Uu+v8{w4x<8 z{Yeq`Hk!+7j}x)zN_Jgy{Hp1F6#G(K!LA5BMJd{s)+BxLS7D zLxOe3k?6S&loDM8KW9l&(U99-VIr02xaoCmFf>6nds*V+Se`gxa~#T!!*H2+n6Qvg zp+ZyoMeDg{olt*y)*dKsHXq35?YcV*{jx-uuR3AZH1ocvsyA9s5 zHRGV24u}FpE{37>6rma2T#+I2OOER#mYmicMEMI?TwkqG5ade6$S7 zYZCq*(dm?{hSGB6sx}#l-1a8L*yDV(RPpZ^5D%+m4~oLx_kJLVg@2WWx2+DplDoQC znPRm(hv0wueP5QjAqZ;S(hJw3xRWDFzq^S3*8KJ->2X`%h4qx}Q4|%oC^v24ZyhGE z*j|%l_cqOX9CsA$(^MbF9sE8e>7rkk(H~+{oecR>Ji+X!Wj&}Fqh*xK;-q+kp1fta z$DPnJ3LUFfH2=7gtD<0c#IC24M#tUPrCy zPl|sz;v-0c1SHYpWkrO?Kj^rFTHR@T7{Y2=MhpgT8VYt-s7f=XQE63Uj%%|_=D3y9 zp*@iHKokWEnh}TtLF_013>ldaA0zTp@Oq0MxdFkyaLqKc(+RaLOomBkc|uWGyT0J} z-L`*R98Ip9W*xWNKy7-$-z+vQrR6?;X_lRx?31SXNUB)|bD3U_9+O0|>dT(-QG~mW zmD`|ob+b;qWjEN}p|_J^td?7xNTC%C*=-7ai@4RWtr{-~y>6h-8h^|E;g+Tiz5Q5*#K z5M;IL2o10N`OL@@t7Dx6`E!Kfb?I&Ua!kGL>19Q9TePMkx2;7G7s1eadP*^Ljw97P z!S1iBCOC%S`G;-Wjk`(mS5ws-|EHOsCf~Mt>+b3ra^pT->$3dSbw$si6&r3&orXCed#jj{h`;){)@cufQ(?aCv0li-YMds+@*;$xIFnS)jYmc7$D3a1M1-Dmj z`$0<<9h+MA7DM{$aMmSZZ5VO|RnEFUfe@#1kj< zl2p-h+-;){1IR=17=Qbskq^=Y+8p&EQP4&!|wLB8|-4w%gOS^OC*1^!fK}@ z$*wx_YZW_F4CZFD4S3C?!_S{v^ptMZ6DGOFhtLdX9jTV(Ade}jg?>9M? z+_eSN+kmB3J~>Q=TQ2K%84Y@UIm+PIa+=CzSNdEfuV46kgof#kLpz&LtBU-741R0Y zp}Fl-l0E40pSPE!{Cyx4HLZVXK7#4S!AB6Jo+USB>G2lN?mn(~e$L(ervg;?m=QPiMs_JFg zpQlQWP46p%V7qB0H*NX&BRsyMV-ISYTD4@iSN^@^vLaS}*|UkitKfgPnqHQ~rl@!Q)iXa$`z+@%1gYl-HS-W$GmgmtnLSRlrlyufwQ7o= zZ&3o3c=%ma+wXHfPaZ)YJy*4S;^9;7JAWRlb=YmAlb**Eb@A)T5XGt@a64z2UzRFh zu#g{giz5Dgp0X^t>_~r2-)&>J?d))P(`?c8%6)ebd^1hmaNtb4#c&_T62*z}gbt&1 zP1$&luCxcfS*Oi1mb3AkB~7b^ZZu1mG7<+Rbd zhTJ*Ht(#PpH_uhmbh13Tue#ZgAShaG!^Kk7wyu{g(d~H*hZ&b7a?to!l?CLhD1lIH8gE$)b|BO&t*C#JpVOp0kJYl)9HZ!YDMf2;i?vQq ziX;^!sVM4Yc{O!EPfyW#98*o3K~JQfp7iX`GR5arv%GHFU5=<3j`4VlmS3PSRtNA$T5oVV?|F^gD&eSugu?*_Iq1sp%U0 zPBLAGU{H5n3LhWf&e$@%#OG;^YWo)&4v|Y`TJ3nf_++*;|Bo8mrPLHE$98 z)AO^UBzKM3GYe)mhsy~~q?AYZ5npuC9WK5tKZhO>JA4UHp|25Um za~4CI6nWI#&C(vn-6TnHdgbq7dk$h()EuksS`@#&ntD`~N74E7WY6=_bT3OEHE(i+ zLohGN(Tc(#ASMbFCI4@M0vs^{3b~!mha6Gt`YB1c!)ZI68T|V6q+zK_t=o1wxftqY ziHoD`ef56_-rh%doi2i^*X7goLjh85HoGW_R#lXuAoK*KYTC(2_Phn;*W>u1K;dTD z{dprd4NA}X`&La;xlTpJA_0|1bS{J9t8J{d!QOX|;@f>}_w{Rg@)3j;+fw>G z1g}M`8bZ&P(^!pTYSR`EUvkqaS1qll=N#vc`_+HbqoVTnrJar~h9~=-vfrzBb;Yiy zBwbCT!LJU(R?~VhJh9~-CL%6>uWHrb99wQW^qa)*W6ul0bCdhRnpU3Bq24l- zoR%T|DSfTd=Q6jIASXtz3xZ{~`__$PH4T-TvDtme?rBxy4`e&9N7E6@?&3Iioy>o6 zxfsfxXNqIkp0D56f}u{9pwld&VP5=buamt_G}9Y+00dZEBk{?aTc?|-Zlbz*-eNd^ zpk10KB9e@f6qxhS4(A@j7ZRw(%4CAqrWJL&%^pD&+4_M~ZlmbeJo-L|6JF1X#tF#dl!mg~|i6QSqHO^4)nE`~9e>7n;YP1DP+h(J7= zy6!oK1Zv?@sphy9x3D@6t>}-ICYO~z@9b=5kb@cZCixtrU6{;**t9N7(d&xv`?+Ou z*!kvoYSR?GcExO}MeT3t#>w88*YSw~<=(1gm@*+F=(`$yL0*==KH-)$;jS^<3iNL24>{+Z}GO8(tax zDEMX3G0KPM&$2bYFCRW6r+u~LP0?ePuD4am=M#KR>2p^tgWR-b$ETMCp{EUgy-AMZ zmITzd@cPrPue^RCfl7FuKhJ-96m=B^(X#2TS2@nY>rlJ)>8Y3GUXm-nu4>gc%fJ&l zm>?S+C@3hp9ygoW17Uo=C6`z1iGSOxbw(Wq&}JPLvDV86wO9V|ARuBw87T zT1qAn9Mk6+s_W5A<7R4)vp30= z4D%()o2JQaT`M&&*QjqoNkt=mz7ZhvFvt9HT`m2v#i}Vmu;Wo zH_bXW_@!F)Mak2GE7Td(M6xYj%m4VOgRDOpB2xX3Ka<+TwtklVi(qDtEo%I?Z6ERBbCl%U%3-66AkPkp%R^xLo^bw&Hfj z2up^FlyXs)|8CLm->M=Rmc#J%IM(bgpau&fQna~h4}H{`OrN#-LMO3ioM@RG#IF{G*_R5ek6d?-+$+lB%Xp;)-h zw3zE~as1JfWUp7KJ}y6gYTf+vS}r?E&HCH$@EfUVDt2M4N-R{Qlb_$slFhQ-BuT9L zn?i>$L*syV>+>J9nB48Ze(+Ga%Ev{4P|a*Z(;;$4L38BK%*dkHv0QuK>Y|M zL8fS8(Kp-h<44ltH(XRqHO{LpkE$;@9>M3+dhX)KY8ziISxTS~K6g3JH%SuRUit7V zo}Kl!r&jIFk;Vzh4=rdyEE{v1NG7U^XmuXz86(cVjCa#%O3ACc2*M%vCPA>~ON01qc z*waM6DOjf;W@OmUi$kx(qAK`2gJn`d`bF#*1+zIBrsP*I$si01UVa&+eGbpQpYVuo|%wea@l(nQSOSe-y^q;L9YgXKmROAaymxDm;8K- z=k?TV&2NcKJ?3a~(Gp(Pq4HV*Q<>)3CP~klhU|8TUhgZ4ejGf4F{V%tlo67=Ho88j6g0}E)tx#x$GY`5qfPA`K|p<>3Ch1cKH2Z0<)nSt?cwk4v*Ziik4S)J$hLh z^jdx$iI@`y)L?OcfEg@SoJJ%#U4q?M4A*TdHz#tD>q$~1zkRc`!LQ9UcjG|k$%`ZC zJaJIOMRA1RX;df_?E0qYm!aKpu$J|sXnS3FVgNoVvV1%#pg#n=BX=#aX-|fD_~~Tm zo8{bbpwo2Sw_cW@6jiybYo31;g*lEdNnH%n4(ATR6#VvoWf@vg68?5Ek$l(nys@W! zRc%{0Ehj@CElVwWqR%i`CY0>{E4t#vGQDryar4)e*fhkBNpf07jI~0g(n%7Oo+&$y zTGJNn?qYa@-^pEDG(2Mj2J3sZ^E@ega^aBt4y9)edUg2OqzIEb?LZ?F^L%i!j4^$F zm{C!}#tR{TSjg0Hp+shQA3+!vswK7J(Q`$|H%MANP+mo_aN(g6Gui2z1Yf(If^{m@ z^=_6YJ6+>dvJq2-*fUFh_i_}yEKDmUieXW!qWt>`XfLs=KaMRKwo4N2xDO1gY@7~7 zpA=2*c}3}0(e0ESclr2!dg^CMg5S@|_Mfz653#KfUG0Sq* z606#OB&l+o+3AGXR1Zb^14R&!Rnz7#1fgn4UFWWAvkV160ZP*wD<>-`6RKr#$K7DK zizB>kHVC#^_64IRZzDXHv5XK#)klw28k zR>W2)8g2pgHbPb*c#MnR%xR(2MA0#djze_3h2ya}e$o@>c99azj6fpx+>+fNB}=Z#l3$(_|8oqXUlW^L835ZddyJHN!6%Hpy{Oio)DRg3~TY zADNKn1h1^PO}T3QZKju1!iSEM5vf;E)60_R_K9x8=$9myO>L_3;cu9KgoymTJ%T9K zl(7o+KzZ3PVd*ewwS0P840Chj&(a3J5Ng7ZLL|EV^6NaP#AuYbQnIUp+ZF7>-PWU} z$d2D2f&E}HkrDkPH{GMCeV!#*4!Nz5mZkN4&8~@F*NCNTv`8ucE`OeXmN`;JN`4P3 zuVFfk(&r&JyQD0;1eSA7g^ z5tKns#KW(Exjw4OW*rr&k_}kv1*%k1$Efwp!|&Cy((8)&{o8Hx$DP`=#m`fOJSaBh zNl-^i+9XX(BAM5V4#E2KKB_50^2mVnmHr;V=lJ7R_)X$}0r@I>i=k*OPwqKNOZ)TQ zuM0b!vD1m|I*lsjNl$z9J}F7U*CKoUs^in!vRg)aTN7SK(ec*&_U1@p)6&a|{QgUe z6@p_^s{+vyLgR&6@#rra4y9#pjyoBmUN%RIi3zBep;BVRj2A0^k80D9IohBXTFn)! z5R2%~%aP@Oga44?qyawUP~wIQS1x;4(_c*wd(tR@sd%+awY;+9ln*a*(<~l+$I0u) z2n&|iJ8J&yI)9$W3`1-Ax?`FWNW}=W;v{k@h3Ii)&2dY1N2rKm&z9W6@L1HUr1%xh zaGsu}`As3>UyS-3FVu>!$MQS`&+m~$J6X8_G3oVxm{JIoqVTrqBw5nyHAq}CCJ<|W zXO2@rHYYbev2X2lZPvj-uSHCi!=)s}D`heQneO_B=e)bd5+|EjHX5Cxrd#|{g&+XsZCk9P%bD_ZkE0|qWC&X=pV^#Q=9f^sjuh{ zyC@}p5vry~b{l2{3c+&|9D7pK4#8Ut<*#eW?hSTuwt4AqAX-d(Xh9j-@lR^rVR&*l zzlshq;{^$ZYtT!%sS1CuURMu6P@3lG35VVZh;PAC{p9F_-bpP<`nx3bhj{jsQV4b1 zbO^HMSOm9Swycs@R_&f#nCcQ4qs7K-L5n8%h_I?}-dTscHczO}v z_q3|VRejYoy=sYXi{$hNJ!SA~-FB*;dvj#rI>C@aCVVb}(=c4986`239QWt=mBsQa(;#Cw^|C&oNjgk{wSO{U&!kwP{Os zUGw|5u`QOX#=+gklcS8($Yj6gt^=`u?TBdf;`NaI7P06ZemhCp-zQ4fk;>*`*q5Y< zRfC{m*+B*c<+FcS^e)NYeOHi}z#aLQ9Z2&RCspgt%6yP^&rqQ8TovetM?n*G3A-ewz09 z$&C1yUuU^1FM=<(z23H?A%n6bW(B5YYVrB*zWus?qIh_do9f~hZyk8kME+g{iO2}(Pu1}pg1&mXa@$YQ z9xYW!o)(m7q}Nk148!e_%l>Iu1dJFO70MLDx*4u`ZW6sN#coTte~8hdG{dU7P2p>o z&knogy$t&iB&}#pf-IbV{hppZTXwvHTU48}Vpp`LzG>FsmooC8Y&q0_qBv4u3D^N+ zh!~R`B&!vx&Ys*lHBtPm8r)`NQ{={ZDTnc6#VW0^8&+j znA>j9OTq1q7nU7hR8~Nq*KMET*u$njXj*7qsuV5b;Mb4fX*E&od6L^3FDWLW5J^Q_ z?^=S-E1o^Xj#F?NYERYdHsJ$@5HMoIcKzh%SFA`kLR2kGpp?CT)=AN`q8Bhg5kQDR z3S^|Tgw|BX1gb%ziUBLt2r;opeNJncLj`376rex~*z09=dJ*E{(Q-n77b#}ER4SMS zq2z9kDPAlfSkQb@lqIt*pgrZ+W2itpl%!mgJfgJxtY;4^WOlfytbBZz%%*s35uH}? zb(x=c@%8^CQ%JY7$mOO9DmTVKS|Wg+F&VB>H18PH3|9vV=@E{ON|p4 zj4E`}>$wa=^4UbUeUnVdZL5Y|LcI)F3dOU-b{sU%`>U3Ji2hIv+vArq^2D%EEhbV7 zw_$lLYSj^owrH7BphTxw6~X9Bk5T?yMkShI;({UK;!~7W$1ow!39r9!p;~tQy5rNk z>Sm}S+Feec{1jpN`{Sh5W5fj$^2nG#G%V8Up7By#;WfxrKPicF(G$Lo*|Eu0QL?+2 zqbY7*Iz7UF+a)zsLHd;7wJ(OYxAkLqhoWzuDpDtzRVrrFJYX#tC=*Fd_0N;HEY>=8 z*NxP)r8LsnRtCSAuD6UrGN=)a*QqtT_z1pT=GtieH||@ET>TrHC`NH1I7z!1gmG0yP95= z)TXDnO$qraAy1E*GD1!a$T8D{N^Av(S?*T_hx-y#0b{iQ1R4m!h7FiKBSB+gfgWTa zv0{aPM%2e-HyA}Iyh}>d;!?pzq1gX> zT9$>+GpiE}$|SPmFkFWD`Ol^$xYfZ@^0R12PxJ61Vt^Pal!!3{1kK72ELd`^h>+yi zrQ&Nr~Y&-fb$l9OG4z5pt>_Qd(k0ivxIw z(c{I$Pfyov+uin)-$@>`eE5|Ss6|YF6@#S3!fl%@S5&GceDDy#=@WiF>1UeMe?*KJ zA22E-d%l;W5HDC1V8;y^muf}yG4byb(5_+h$vAN}ug2e|67cxX(E?BCR{KoJY zMz=1R9kHkf9e0RXaX=A3oNjwWr+u8fYOqK&A|54&Q-E+9)u*M$KCEC_0tbzk5a@^( zF%a;p@A+dKRFE zjw5>fF!8z#lNJ&%V3e3a!+;+*n&4qajOwJaXB;moIb2{`IKD#qg?JI8gpV9JTvArB zPBuzjA2(eo`AM{Raj{CZ>Nn+czj%pcyh3P4VE36X@mlURXW+DA9UMQH6 z$5q3nRz#UrHZkbDdXHFu2&}qh5M}~^zf<?2wV2SNu3Hi}7@}q@>W%LQLXkUgNU;+su zY}gpZG0LakVd4^k#Yz)9$jCqeg4jVtj0tvAbbN#j7%wEzN_Izo`a7o6ETEp{qx)zf zc`@>`$xtQK&xom9{(T0NzgUT2Odb@_4nz87NI%I{{}olSI=yhAXjmm07iot}Or`Xf zY#HX~GOU!YqWy}-m_jtAKSfA?;pY)vX6?1eev@3bf{tA)yKRqgGY5Z5x0NFP>k35JH%QM)H=mT=gZhD*lba*DFAK zY<63%Gk>0smy{M#N#;KL^JtK;TC_?rr%%df-$PK=Iw?pW9Vajt9g|jc1-G&KRRQfV zqhCh!lh~AX%M(xnh#+LNV9m42Wk>dUgs)L}I|hq?M6&0h^xPq4#l}k|GP@`G%<|(U zyWN+gZ;mcpsMNcL#jg$$R|06T7~-WQ zhpQCh$xEp@ciPFPM*#&UFtY0+)nxhLOWc?V!2_1~00Wi~V#Ujgj{9<4;aY(J0ZX7_ zID~e8XPA5gyny)ur6e}Xb@;hlc7zQWMa*z9gBq#qxa(C*YiR=FQBoq3(x)xMlgn60 z|4iwFvfDEy&kmE;N^hIq6n4iuPGYc|9?@qUBrPUjqzEDgMMR%J>v*DBPkWkR))cq! z$NjC^B18p542lmcA~#|w5&QM5qYIUo2`(Ui0U9qaBp{CriATbPijI_+iLaaFHsq!y zpFIg2FnFYhf$_J?h+pyCu^KkvZIoYE0r{R4oskmKV}*ppqld_C%TAl$TUe-45|{nA8YH$;(9uONGmHBKlE( zH*5rpl^!H7mHsBVs0x-* z#Vp-vuh!|!F^L^GSV|<(IkxaM4Af@^#GlyoQ;sFov_Vb<^l8Cb?IQS!*%f|f>FpXX z(}}-(&=ViS(d+(*aWV1ueo1;tkcF>dM7_#qFWGArPWx#|!rwYdU@ZUMf>jECB1MXi z6j+Pro)PscP$1WC>nyrrRa#N{$o%`up0C{22EUl=#CTez+aES9t*VSwXb2S;0aCCW zLE}<-1B*bYG&1$&(vg6GfJ6fQ0TU7c06-8D41~f_+0ZV7);<&f1d)t9;A#TPvS84Bv2EZ zgfjF=?B<*#Z#0v1OlOkUfF@DQc#^W|PVyVTBnmT_q-c7RJOiFYZHAMSO=ptF047nI z(IkcGPx2e!Bx*C5q+mLeyaqIhV#bq{O?Q&t047nG!6Ze~o8%erBx*C9q-;8qJO(g{ z(u^i4On;K!04GtK!6XHL)0yNoph*-no}_HLll%rSiNXvfDVp9S&wwXUo8cs7)0yNk zfJu~QG)ZClll%raiP{V%DVWYAuK`V>nDHcK)1BlufJqc)FiFw$CV2)tiP{V&DVxqD zj{!`gG^0rh)1Txwz)93*FiF95CV35L62*)sDVy#jzX435FoQ{dil#ToGvG7aD@k1wdW;yKN$@%zk*u6G8W5mU=Ujgn;E-nweJ&% zMa|o-Z}LoRwu(wLSQavi^@LndOUbCVGD@Gp zM_1D4!zIngRa1r)MNKiq{*okH(F(N!(@#o_j?_o=VVo}MHsPWktA)08OpC4pP04pM zage}&*`pSe3~+9}i~1-eQNGjbP{-K;%P3RM{5mi}p##>ZtdlzcmIHyroAvQVrx^2p zK~Ei|eCG1b**J0u7$lDTtgJPM^b#hXw#)L>vY_l7|B>UsJDs?7ZQyNsfr&R%T@^Gh z)ZYrPbLRAR;+BI2y$4Leg^pZl?E$u~56EPH+5wyDt%`%}dJwC50nJ$6OpN2e}GJ7oL2J^W0O# zNdiKI=??gzZSg;Qep61<7@Tr!FA^G^MM01lHb~PH1H-f}2wCeJ9J4EMoFZ?~(e`J5 zSo2_1Mj)c&S0077>cN-mnte)ZS5a%D)VkKvFjGVFJ5eRkg~&jxw_69-iRZ zawE;MZ(@D}ByL`avn5!IDi!QS5gs844*0ikN&FsKbbU0qX(?!pXXE*!5V`4Va{brmt+i4QNK?nj|9R3vC4m@pc znpX&y5fVW&Xu*^1;(Zw{3k3;E%`h#C^|TYeujpMn(ib>XoOW%IO!yz3R>*>X^p2v9 zu0^yH;sKK2pDbo$bbj(gXp`JoGL78P1SFDrNhkbk`(ih|lSrnqSgWVAsW@0218Yvv z|C!1GKRu6z2aX`mDUlQ$dsO~t!g>^*z@`;O7-(ifF)$))=yp+iNN<^ro32|3rKd>p z+69}412%JzPi_tK)Vbe~)jy(tJ3dgMEx#^N7w{!4?v~Q!_cf(qX-#%_<$8!5D2$S# zx^z5eG(^c&mpBJ%sb*&Ai*0Y4#XhH}No~?W3QQgP7Tbb|!H517Pj0Js1-4l2ULtQX zk~|RjQER0mS{-UDka%#J<*Ncbc+;Vnz?bc;PFdboMg2_+#nMy5EVlo>s*+Nv?V+JM(jUR

    xT0autkwzq?Oc(WtNF`#9Ve9iO__UtV1!PBCUYKDDej4V?IOcjshJxxqgLWjWr_ z#v0ROk;000LvfZjSHV1GfA;Fab6V4}-S13$rd0{jlDC49@xjw+2|qChN2W3@AGTwf zgJ!lep+~I|H-!IOU3fF#TH>KsgG=jiuzv7K!8V0(qtV3DX-glYWW86b=t$+K*EC}B zjg_t!Z*Enr{&*7M;EP>YTELl6LntZgT_r`0Q`)NqM2cAa&1PnwBhvVX-sIc}+v!P5 z!9>;M@T9J>#+J>`N@YL+m3z4U6S^Usz-dmN@?bXN{+PfI1t=8y7Bc4sayB{P~CN`3kd zea^o2Hd<`mLTK*HobH{et@-NxlD7RsbTcYA#Jv;e<=12eeBtli-xtOT9IhBK3D4|( zQ`R)jpLZ?0@p9(ENh+>}J>Gs`Cy=mKG<~qBzuu|CGK{iO-AP_e896lg=XIP6{}4T@ z1N4QN@3q#yG??MOKKSWl_zONO{KidCW5rdo+@0crUfZE8VfUSdg^{M(Wx;&MT`R(L z{)pw>qmGy>kXu|CyLBg#bp^ z|G9O;s1x#(@J~;1aj#GNpVz9oJz@1i4@S1zkZZWBZRj48FpcA^RR6B!n=Htm+RDIN zr?aTyW06+rYWR0CoyTx5eaoH#F4E*9wtL)`2-4K0O$kJM^9mjVV-# z!^Poex+<-j+YIz_d`a5lmzI4`;Aj0_30oLH=LAD_8%tZis<@1(+z!GM9y6|+}U!wLl{ueyROrdFO` z=qp+N)*CTjdualUQ>glKl5yy!zva1j7y9>_-lmoO9OCb)$W`B0xCt%Cp_6LiX}Mzb zVZX*@;(A(0T4KY@&{yX8J@pec*UmP6FRv`PYppx?CF>;rd+lQXu;?Yp)+6WqKne5# z;SlY}zW(C{)5CX1dOL8Jw69tWO-~Ok&(7RP+3Jyz0q-k85^LW6f;}jXYLFk8qjy@x zh0^!H2mKR#|Mt{xF^oeCZ4_WUN9MWDaSz1m@zrNXs~mk`ITQyuC=w*Wl{Q_ z4AdG)2635Pb!{GmIMXTQCK znU4JU@Cj4O;eIjQW?nx3EinV-;~Y;uTh(r0xhtjW%(27KjLNXYg||7q_g1&9eN3!3!c;a zpUaoto}CUKnvZj7cJ&!jF1D!5A*4~78ZlKXg$cv4cw~M$2X37QPdnzAN6dvpQ1Se$38HrY8D36Z0(;>rGpo}t zk44G;q$I)E!5VoxhD7`gDcL7$p2 zY|u0wkjkQGXD(D^L0+2J(A$154bI=^%=Kq=>^s5AI&nB9mmuk`6kyY(VCdvtNI^7~0&dd>3tfkoMuue#n4mS{h$or9j<9Z@yXKBCdF zLV?*@3lk!Z&#R{}ZkX=);hul{pUFuICTQLI%uI$*K#X#JM!$FH?%|P|mEW4#_?ETB z@tzmxpgO^YrjiwLUC~~jAR<5PTtgI_Ug7+ON$46nI+^?Z+KI*=#f9$mKXlzjhx_hV z;-MwL?JU7zQC>OhSbrM5mnmqRgAwbW4XRMG#xD)L^3of~^6wH{WX_i)HVU(kEcWlv z=4({;r-c!Bma}&)ydv~2zRpHIy;qsLc8%(q)%9a+039aw8^T%My%WWFaY1NRN_f^X z=$0n`DK#r_j3fSdlT_Vq^P}w0+I+b{xSCEP8~l`cKs?lIc>u~-ApBZAcBx{%>~aPA zMf;#&Ez@hH$^F~jnq9v?+Mt}m{iBdFGw< zr5+)7`1db~^Q$Jmi&iML&GqZdwUAnT`zq^2LPdY1WlDyYHqKA@C;7wctlq`O2zo;m zt5AKVWj!wBT*!T|Fa7G1@Y@%1vpPp`1vz3;cUlyw<7|;FHQCN z{2Bn}PlY_U^R1qxEwuUg&-D+(#HMc2)df7|gzE`r?c(CShWE!JJo2Y=@}lb7nITsa zZK1VK|NQe2Yt(mg1v)G52w&^|724laa_1G7&7Rtfk8u zjLJoNNJ#C~#^fkrMQ5$ZZB*b_firWwJMO}AHH{c{uJWm=*}tf)TO1y0MI}D)%?#+> zKhRULI&V4r@9J#))@=$8?c!t668YSRHpsEi`s&nOqrA3|B|a_{Qw zcKy@%^_tkMXvNV+CfYTzts$s)cpZFKcE9YS-Ltljx1X zh)V^uMic7{fe7@aIi$K;YlPi!s3^2y$StM*Ljyke*hn#DbJ_G-@ycA6Dy2kS`%+hDyvU%k!=>&@=#{YXyTz7A z#v5Onp16;#IiBm6@oJCXSIeGgs@ZD(67|O6%V+(Y0q2vVkT>$u@An}SpW!q*1(ywa zu9O|f8+sP$U;QjGS{c(tn~QaL^e)szklLSdqgva#dXO>Pvog|Tyae5He`A@_eo;*P znMP{-Y5c&Me+X&y<6-2`fG*(bShehnkDu^sPaKCf9-VB`UicavFmk^Bx~JLeke&gr zbpJW}EUD@K8=F^mEz1u#-)<+}0C-nSrRVZkoNKCT8&AKCKc8Z!gxlvckLEe%S%182 znxkdaUls(|`}3@e(ZU<~E~A5&yo`p&#pNv#Q9oPak}!$0Fb^?)&bMmm8gcM8V{s*T zq+zz^1Wd)MaiRL2>lv4Ft?8RB+>^q2VP=$!>= zy(@$)v+`pso>9iCc(sye&^Os0UP6pG+t8|*^lz;|6C&u@aKyUjL*Z>^v2ERibkqJmNuV>jbVLt zs~4}EjI0=w^8Kfm{;{=IAODj5;kqRN`h|F7XE;%WJ@HF{RN1!RhHtPWly0oerP^9` zL}!eXP5}J+o4vo2Uey8bUcS+3SCRz~w#h3cFG`p-aG_tuN z!BrGl>KsDlyC1|1G!LcI{!x302Z-mfr9`s-sQjlgtt)F2#QC|8)!oR9XnM1M$B-^o zaP*q+4&`m{o8mfUOHk8QpY#WPV+#vpzv${H^poYvd`gC&trc}Rcm1PtVJ*38J@ra)dP1Ga7u#dgjh?O* zjLjV(%~tiRxDzfs)A4q9@DeD#{cx${v-phpCt@x<>;>Pm? z)dQZKA5V*eqUX9UUAVM3wtj7OyzZVjuV#7?{O8;ta5@K4(lzTFQs%@U^cK4P5G+<| zce(X(s)eOQ(YZ#()X0n3bEU5atP88SNva-ISy@~oaR}*!dtKt`g`#^yN>w8-nBjR* z(50mdTaxg#A2VH|Pp*Bwte(2)HL}#~@#FUL`25RWla|?%_txcOhhc-_zR`cvQk!%(#B`x-@pZwE#LGf@wM~dj8*;Vt z@!MeoSAbG#6{Pw`d!oEtnfl;3f8W@O6Fd}isBj_|GKG)+q@;Dl?I&Tt>_y1S7$ccO zjY%IKzD^PQXtnh}w{>%<#MkaO79UamwR?P>q`pQxXxYQ`sm`T0kV_b(POB)Y{#)(5 z>o;4wKPoe>#Djwsz1!t3!8Ywvz7EpaEw|NqDqkrk_@v6FEinx1KrAFBeqa9e;|HX( z9NRW+d=w=%rS{yim#`DWKaGIq|2aLi+34$l`9O}j(c(p|H)X5wZmn~Bo`@HH&co++ zKJ;GgX@0MgS#_rKj_AJHOlR9wpsI$_tBV#_%%?lo1sPX8vv()exL>W6-2S1(+VDta5FNa>fjKl|#rc*__qbEq#WCE2gNWGQ!~+~|{k&0A-0``?3i?yA`3QNjAu)GQ{sAAiy` zxpZ!Rf8)pM`?3fA7;SSUFZ+hEV;Zy_PDNiq-;n!uH5bJ-yHQ+@&Gi3L8lm9f+64#l zwD^(URY?~OW(L59vGKRctV-+(NaMPzBbz8(hKb@I{ZlKXt+lULPJNEwB{dRN^d}jf zXQtqA(nZj|wmQ~!Vda{nFa;*=x|%vwPdb9GknZSHk;r;?uxs{7n*s{;^v-3mc-k*Z zLRPB#`a!R9g*PDxf(=0tXbx6VDNR@!`k=y4<9_u-yZwKdJE;S zW(J%Ncd`9236j2l>v#)naFgN2i-1k|dlBE(nGzQ1p7y&u*CVggW=4D?X={0GnFV#C z(CR$-OhqZk@o}Gvt*lnZYnoyW(ol>;+7pH=aU@IgWbAwWJdw@7*yP2{DeG|u zao+|qOo%AUADTJm*oz->4p&R)&GuAKOUS?MzimiAxKmdY0erJrzvcQ(uQzorfBZ&` zB#sFWz2EXXVX9$A<0yEsRsZd$P~5@!D+TLk)YZd<&xOx$Zy_4lExrI^c^`Kn5N{2k-_7Txj`8h3Lvy7)0*pK3`T=W={6sXzYCj9%gR z(I)$w9`!jDXPg$#_2PH$e>79k+(H_j(3mgrJaA>K@nqW1De^*z5@!cpJUVag13nA%|-py z^>dBuX9oQ+hmX*FUpY;b4zGp3mfrU+jj@RVHx)UI|1`I(pQy#S1V22lvtL3EAvDs2 z+*Xi!vYS(D$i^4)gkiSuGxVU@w0C*dRk;;l0%H0=`(2lCpHM>Bt4u zD3_}s#95{minDe9?{|fJ5e40)+XMt=P?y{~qR#=}&~T*zenF!}8t3}vcE3NzNU3bT z?D|dh@^06smexIw_m~CldAd0pL7x0|P!ijERR?)UAu^%mhx)iG1*dxUj_fW2>sTUbky@$CT~!q#l9oW zygBz2o0Yk2_7y&(yLKn73t$Z%@Jjr2C4lm9`lFOV$%L885s5ep3;$S&`2Zk2WNo-D zxpOqR?ZAh0;(Cj6ZqcdbRv+JU&)@8{-+AP@mE*2%S5>+!pDOKWAg+2Zb5Z|mEcBzQEPmxps2Pc-=&}BLJ2C ztd$|5*Xy|bA&hqNnu{1(cRzE6evZzZOsVp<`kbS7>wfFuKv`!(JU2;+(l0q&-%aUG`f)PsM4-uyyL!Hxb9coBC%%WMsZ~6VGv9cM zvcJL8R^%g8bxM%9qI{9NY;e3yE>5{iyWlEG38Cm<=mR=gZEvtyOb)g_8kYk8V21DD zRoQ+Y0bh^lf8kX9XRi@aRW;kR0DQkngPd|k=i2uRfTS@PF$in2!Hk?kGb zjI)ylCTr&g#D|M?62#rQ=^xl@H%>kM@b1>^7td3$FKd{nxu>PuPw#07jd7mpRklv| z|CnW|cYjOf{%1FwvXb9jY^vS2TUEwGvt=#NUhSpi+SykGsS#QpfSy}I8?qigr(JQR zV|0DjgV4D4A4Qdh(uK@#`*~pRyolhs(ia&g567z(8j%WK(#%{t#np%8#@5I-d5~7L z>C;QHi6q0{?&u>j#P6Nm_WB)(sXPq+F`s65&^GUoq8O4<)-#q=9c@yBCjp_!Lg$q z5bO-K=-rWlv=Wtj+F~RX@@S13py9Z{Yt~mBeA1_!Lp;PQ9xXL>-EVi}cY)!fJ!_A% zTR->F(_qK@O7Ef^k4FF2d6B;wx;H&>74jxE&-v{mr4PtwMb24res!Q;baw*fa#UWq zYpRKjTE6mdW5gRrJnvPhFjVuJR8c-_R1>zHb$N!NA6(M`Y!KVKKe=~R`poFG;&!C> z%TlDBW2NTc$s4E%Z)KsoPi(_Yl->SaTf&Q-<9C~V%N+lx9(M0ardHX6`L6KmI~s(R zg<3xQ->Ai-Ww#wY`Kl1vsSUF;Wd5tYsakRDhH0b@zA^ zUEkXBr|P$M<(1N+#L~IYi4ci62r~8AmUnI*{@)ciapganhA@-{5BlY!*N&8Jb$DR# zN~8Wk?)J0ykb5~&RcW*GTc`@Tx?)NGzV|}AE`Kz4=#FU}!x7q9)aLiyT48i>Zuz&j zg$4zg)9WiIskRUMt$O{|M5F;rwf?YPbH%5O!uG^_n?t0&?-SP@k)UBDAN*L@<0&b? zDSRrdL;Wt#^}HSP$MZ(ZiS6_QF7K{QpuML3qKbYUg}+R-G}jv6j!hLPl@E9-uAbHW zsOI}eLtVIp+GdcdN9gdCk~tGSJhCep3)T3Z-WJzRbf5K!raBk-MfoYXTAt%-__P|Q zR&(NuER6&1TkR0PeUwM}p=DxNazU{-Cd0D-k<=#%&z`SGHcfs%_bWFcV&bb&S$uD1 z*HlpKmvN&1+3c9<=Yb~1`i8~8@dQ`P?#MfXE3%(9)*~%m$6T4(T?*)zyRa$eJ5l95 zuTR@6uxfHNZu@?=jk@9XH#P=3`R4AHI=7l~F!qH^Df1YEbaCaYKLx|}NBXMoFz)wl z-^DOU2!EMItQG`pT`ZrjU+P%n&2qEix;NsNb}CaPxw1vl)#<&eXMCMsT%q4GT0DDaAcoo#pwRUmKcw;U#$1+f zffn{a$X>SV3!MT_`X9)0*QH!=fuim~-s74Gn5%?TM<{OmP_3S8h5`Y3CfK7U-g@j> z)V8NSS4KB4YOX6Qpzf6)tID%P7?h*{E`NDyBZ1bp?%Em&px2qcs{Z_SC>>6>$F`UIG;XRUGvST_w`W)BN^f83OkDJ zMX-BOVh9Zz%op!e`{t`=hSc}Y`=>%_f69@DAiYg}2g7tP{C;dYXV>P2&_X8|ymK-t zx8z;+{>qo$TdBz?S4nu-Cwr=L2QD2inHf)<^%0z*zP5R>rwRfxZfF4tbEO7PohSL= zZoU6{FvGUcB7v?9ApSd-iobkJK=+5z5$uJQRtQWzcU30l%0ac?KZ|^TGZe(3( z&!V??R&?gzoW)9Ic~xtf7~Orj%ktg}E!U?Em5=)$y& zS#j*XR!Yt`NvaLkJO2Dy#Z!NHuua?ew4}rSnA^ipDJO5SHh*VP#Av&kNM(vA3R>{1zsEdnq}^wew=viZk66c*Ifoz zoe16X)3&E>BM~|ka z;Tsvl^#2bhK-j-#84U*Plj&s1GvWZyH8!1!@3sro{ejN>0?@fM9ZM(6@|KuqKqH$C zOD7Ub5F?gvx&{dL1tsXVN56yTMH>UNpJ6eCsY>T06J5V6ot;oqe-s~-s7L7x9w50j zwZ-)fG$9G8F;#zFbt|wizF|77D^3n?JtPQ*M8CY7~ffn3UlC`^$Q%6Yw z^`=b}XkHycC*@QaV&HyS^@-c+9lq!53H-(6R z3YMH!qX8dYkVY+of7Z4mB*q)vW~#4=ZgHIP6|y%@ngkG`jWZFSB@aoQ7q7}JL=c}V zUV6wOM|Wk-RqfW!o|(`l6J~N=Y&HBt+I7dsX(RYj$OkZ9ksMmW>J8o$KSIq~BCoLpU;s-K7wjCCYh+ z@0z&;@SR&a#9S=u%2a&f0VN$Az!PUoN{h;H#>MC>B`*J zl=Fkn8voof&61T8W>Mjd7skTkPRc&+j)ieX4l)0Jd`e)e4~8a92CKziio8Kip?#lB z<5X`{uZh9Tbxs-9HA7=5#qvbr#q110PWAf&8J1rE0_3E_r(k7qeoQ(4m#js~NhSPE z+^Hy2f6jG#l@Q}X$ERWXYHZ{Ey)q(G&VOF|Asx+>2JEi%!lC*BX%_nDT|Cg!EHV-o zJ!*Ofj6-Vr@GQf3bSW4@b)i@jGsuOa5Rzn^j5V#eHF0#IKh-(MMPslA^gSZKCdwrZ zo>`Uayk>9-f;izdsqg86{F)S1Rd{?&m?XHqfBrQuO-;9byb-?UEMfky3C)v{6fOCv z5BS$?pcV`UZmafdCcv(e^=smw4qx*%Gj8e+4V%L07iLjaZ2nqOz7e z8k^^OM0aOK#-;(66-zu89*_8dAEqWi$UE)0T1ic{jjyq!BgP5_L=OMOsC-E6*)7xH ze=K@WO18{ueR}0`%yOjOllK_V=sgb;*}3;UHIkp;-?Ja6$(-0I+kejrxg@BEe5R?r zy>fs33U7NZgbDN(PAsG6?1MD~`qW`zQuJeyLZ8B`pu`YbGYR_S(7da(r)meLkxpbTEI-?W#KT^!fA+JT@=r z`Aj=s+2Hx)8D`C>%=rW&QLtUkCo4)ZL_41u1U*8FPseMnl9FQQ(-W)VPsJzoYn-mX z_&nbUbC=(4^VLksF%w|DL~jty4}sD7ZHdk0ZAgu`z;{b-eH+PlqIpgj_p(;Le+>~p zHNq=JV#JxBrB35rx9!8XMQ^4Nsb4Oo_F~p; zB3@6{wu!*6n#OK>M7MSCfWGZTe?Xwna7(De4ZC5urFNJDm>OH#pZ_FUG%HLaGS@Io6rI0*>-Z1TH&z>#jP@f6bKX= z%$*HJK#qNaIqHrrJP4%)J~wRla_e!A`@wF^n7vb;@GS4)BB^GtSP=tNe;oLI=-$Fo zU)}b;We;hnv2uaovNwqBG=t>ecf27mrP6r;~^Amh=huOL=S|E^ZV+Pnin#a z_z?E$Eg+qm4D^4(nIEe;e+#m#B=ubD+#^GYlLFR<0D$AiRa`Qe}RZrkt!`2JdQaFW8MpHh5|rCYmg2Ke(m0gTf-QzUB-RgzIbuNq zO)SIKyVo>=qf>)rlBhBa;%EJQm^Q7m;T1+XMGAy3u~S@Ye}ZOVYJ|T42kREpp=vl(=#j(ABa|t35857$>BF(pnG| z8MzbVywzsGW6%Op5y|amya2toc#X(-y9^s*0#KSmlaQkQrsxLF(1$zo; zej;H>_iNcge`yzDjZ`DQ_OvuXq^@g>U&XAZ1*kZZ1mtOz+jYRLRGJp&oJOgb-eM-e z6syRTl_PV3hJ=Dxh9e{DnQ8z;8-l(Za9thH8kG1_9a}o3#KgK{pc9eC z{ZBq;$p6KgpL}p)snn0+BTu=mZRi|8#NL9eBKdOTe=6QOnqq_#NR^)MEfbj}? zGM-_t&a5a8=%(;#lG`N>x%Cc#TLVTnVO?@q;snX`J?6nfeo{emq24t}S<+f8QLnj0P#mfuEIHgB{}*SuJYy;#r<{ zk5(yopnwrmE@&}>Gs|0fp+T-cWO5hhDD;E8%WCZjjATN2nQifAFQi0>bz>h)`|!>{ z5@AR@bLFq_L37Cbz8Qp4)x&XeBeEXkq|yc>SAig2>VhJLzq_Sa1PB=Hvh*+!tWY3c ze~8xPD#2|En~1HxLDnnEr{=TFj&hRGdL-ZbzLmw8n<=!R<&c#bTriJLD#1a#tA?8C=<2viS+6rt!476A zucv}@xiiu_ycp2A0k>dCB3pT}N16yo@3+Rd2y2LG3(>NO`kVwTSh<&Ern!1?|6$H? zdVE1OnMdlPVdV42*wfHF5uZjWf1dgLE3%E{_eSJ|B>!?la&A27gd9w~XdzfoI5q;8 z5s2FUR9fGe>?0lWp5`76(!HU1qy+bo@3gTfECvT*z-1_&%A$}U zGM&Om8pvzHc#5cD5wk+TXb&j)?=2hZ@q0gCC>3xip8(9Fe-VV6fAwpv7}WH&&LgOa zP)QX4s~-PYfMfNUUO&&W)dj%$JX)(A?FV$J9K4yItxgrbZ;B|iT$iPVhMt3dkeiZ! zgW-D_yhdj2<`&KB&172w#P+IYY1vjti#+MY&o$~tymT-N z0Nchf+KGgy94H4ce~gr^Hz?2$D@uFwqgi=4+m2G^b(*?BI?w3{4W;P0xF7{2dAh^0 z-n9O{Q}h4a4Jpdd{cE{u*JOx3X8dnk@p4r2;)D5#O(0$x&O66Vt9wW|F=om+11=GR zG;xFTg`gT4p1f?cj4A0j)opez@jNa8jbPAmOEM(wtaE^re_Ael?E+y3HnUMHmhFFO z__{{vr1o-tOD*tPj^z0wb+vK{@4t%?+Z4Bw&Q-qk$EP_z?97-L=P{dVha|cUs~bhQ z>#{tX7JksecvU+4oqANCM91|=xrrOjt>4f8dRAp-Y@M`XUXYnp^uYql z_%Dpx?F6O$f1C*jh9vrgB$04 zQ1ksn^+ms}?uh(c+zU*H`R!auHy89&g<|_Z92);fX|s{FPcCh8 z270vn2RK!5P)U*BoLIP@`Nxb!f(T7YzaI|0EeuVYEJSfKaMNhRpf_+!bn|E_ylwQ% zeA6Maf6>U_C~l&nVzGP-EHBoehZN?uH~7c@r9IQJEBWB>2jtlqP{6Z|3Z_My9?uRQ zgtN%B@InAWnCaCR8&BqGmg+TIBKi0Td+OyuesLpg?cBWm{cw3yZc*8mMWa*r`TSED zVCX!1g2{#C%+eJ@JyBf|fun-(4E)VrTn2UHf3kMbKCEAtdVi0&gTnuo`>mh>)GW(v z$)aRBbeXKaHIkT1;N8~?tf}PNXxlYxJCmGy(U1oe-PZ#>gcJw{w=RtG0=P$MPq1ec zaFixT6`**Y&7?3EWV_2GNUBriyTg&yREX`vr;T$xKh|t?$ zIHd$kXzmb$L=GIaWvc$G@kk~1<>XS-6nNp0JwcF_E&G7VI@T9x>vV&v&km#n zns=}t4?;Ke;!z{yM;Y9VR+~_0i~Y?{k4^S28|^T;@*nz{1x-Okm7NT&gF{kUbY1>LqX}_88 z*n75+O0=5eL!2k5RS%-g81U(A0d|+L9r%qzEU^yPSHt#;7+z&(sOQbcQxl!F-FD`H z;l(NPsso@Gl%M$lqq#_93X{{kO80Otgyi(;@UYenkcI~|9Z=;v8J2x1e_;g`=1&4e zETXBEd?J*S6qGnvcdaTvKPg@B&p$DA28xY{s&2x2H)Z>${P96CaX`F(#A5VsU~LFX z(v0~Wa2pA7sOb!$C~`iNZs}Fn^|~hQW$GJXox&6#2}8q8y5|IS$JJ|rJtRx1R{wr& z&H8=*xHgY#5+lm4LzkU)e_;HAX;(ucckkkEol)Ai4O%CGS&%TM&Jt@QWSaI0%Z?(o ztc=Q`{9ZMg)2jV7vlpkE-v9N8#KKrXR$0z_SU@Wdd&^AI=qSiDSY^Ak)pdd_yq^lA zw>%9b-&;YDVW9K;zdBX@gT*y)G=<%rk349ep3w+3<+ZPIp%XMDf1himJUm?4aXg{A z>;%z9gbU6{z_qx+>!cef13@ni#Qz=fPs;f}`-6BiPyIGZN%QE@rEoqayODHL0At9OoeQ4hYIIg#7Ck2_#{oFd6mx4 zd(fd`RyqL(+FupCfBwNpKgl0Dv`-~kCcG0Cq=`E;^xh5%yd6N=iKWw`xQPUzd23P; zkdVf4ranT9pa8YC>9kNr3-Lf|9lx%!DjL@hR+_i3*c=_cxWt?x0}&XZbYB;*&m~dK zIMajbVC+d~@)`cCal-$Kdzbf~ zx(t2h*pPG%>yky_3GMcGYI2!Vh` zC3<0TSV(rae|eX>Alnmw$CH#QpU(B)DVpj6{WaP_Od1>hyJC9j{zB>rkUs-@|3KMy zC6I}Yv&pNfp<-^xtBOCWq03**p}Te{Mmqt^EY%9(}yeQsdYxZY(Jl z^t3ZhSD^!$Dz_yL(5~(jl&2@{G%GY-YDV>CchMnGLW++Bas|0u@e?6Kv2yK-ksd+k zy(3ZB2PeO5wibSQFNrf1;|l7T6RtoSQS-3#gJnxd1q50HPD zWV7EOe>nZYmH9E-OfLwOrEPa8GySSZd>E~O^9QX964%?ZTTj`Rg@VUeyo}A9!~c6H z5zQIFOkoe;%ZDzvE5h2Q#6>xKNlMF{a9XJ^++}q8n(OK#Di|aAfjHnhy4;(rG zsJmgbm%n|a7oYec>cs%BW6?F=-}}s~KxqTOe}d9K&}D+%y`tRsNm>0=&TWJ*ZydU2 z4#5^;?!1r#J?owlg13-{wn(W9l`t- zGcw~wd?m5xQw5my>jl=C3a>x=|KD7%sy`&(`FFjoSmuP&B<7>*EF^Q5S^<3R+fqCg zOt)X1nW}zW^Yjtf=PPQl%RA8+R-K;@f2s|$MT*Jgqh4%J?X9ye{BC+r=z08ElnDOy0Ls)9u&>^^$N=Rgu$gyQm-#(>&6kAwt^DvGxB=Io!me>^Qb z@&hJ*qp^RF3?~Gyh@VCNBp3N-^n{k*zXKE9j1rg|C4z!d>l#_|#Tr9p+`iYdicw)e`YJB*pNcnXOkZVmx^#<- zyN=3a1`8-6J^mc`P??;10(jf#L(s$gFfHo%D4bR{AnN!P@g0!;Sb9^WRmlgAx9!B*V| zJ|j9Jcz6U2b3)d6OjhK42tx0s0 zf(w^v<^~UB`6()gh;;*_MyR$<7oeioU?~r!{ohALEbGyf;%L8+hQn||!^&8$L`F{u zv#7lcRWYcjafwE1{Kz<$ARI|<*xqT0Inm=xu5wl|TFw3ER}mu)f39o_PIWd5$d#(# znpWT=c`DRtw(5#-x_0n*KALk=oIa8Ha(N}-iNm-9k_D-0M-QuR1QO7MAu%$PVv?Mj zMw3gm;;d-{D{b+ph*VTN;dk&(DYfeAw$w^WrFX%9jKUf87A{H~8!ygXBxTMUU(PgA zB0H(=;)&EfP;PRuf1y_}JwqDxK!zI9$BY>t5Lx_a%&oP6&srl5{?V7E)y%Q!X=;R{ zc`6Zu03#0MRUS`oI)TEz(47F$NAbRnqI_n?U1=WLWpf34)?u)=|)g@9H@{*?ls z?R!*K!GM0OBp3!%bl`Oc*h+n4NR;gB@&TsnC2?>!IcrRK?m zDV`L&76|3YLHhaJ4uML)gKnQ-#)Gni1_dTqOxX?LMYWg^-toX{5NyE>mUdpLcQFp= z3Eu$`{sF0#gi+u{_KMcaoC0&fHl;xQK3DGGtWRvRf761-ZUGGg2Bjog^P8-%*HDM+ zBLPiGKn7Fm>-@oU3L*WH#E$UT@Ql~snI!IosuM;?=0RlCT8c|7DtMbZQ;jSnuEada z;K+I(Pm@e%8)dd3IYz*E52@;rTpDtObz#*OII;@roPHTltAb+n-N7wv`;GkBB*3@p z)Zg>$e+Vlg>)_pk?*`>J3Kj2NA3$?|KR&>)(#4F{AS%$xvfK(8y5ncl;z^C`qrVPi z@7F+;S&Jg>E^EK#U(2Y$drc=QR}XxF2-^!7#JA?(&pel^ykJ9_|EGs~pSFBXZ=e3W zearmSB4a=vQ9pfaHO*bRw)$}e$eP)k{e2vKf5Pg)<=)FnDs{)5e{nPZAbF0ZVi5my zI|5Bbp&P(Ur};REd~(2Al$H(&;>7bd`OmY(!tWzeh1W;8IK~`}MetsAy5~!${XwZ* zecTGR^XCKD*6lpfJB7}Rh&J%kpXWv85tirKvCqi*KcPz6SK+x7V6-3iPSxsbBW_>jP_89mNGZm@YyU+25p~tbG8Jh)f&y23t*#JVSirn&pUd1qLbyoB+K!(0H?JZ zxLLr)2BlpzpIN&6%#Q8Zf&XJ*SRoP1E;*`RK(b43$1ow=eNr`?HL2F&LHRrCaiH+d z%x3}{5UUc5O_}ux&@kTvt);nXZUd4V%JQ;$GKus*eyAy+iISsA}`svVZ!M!&ZlpBZt2g@x3^7# zO2y&1PEDgz4+kkO0;cu_Q|2ss z^Q2wGPtBv`yGAYtWY~UoAl%dVe-BAvM($Q)EVYs=9`J%T$ptAn&MAEw`>;jX#8~Ud zXb8i4@Uv|b zqGUOaI9(ppgk0O$CswOqbCiz)wY@%xywg!q$dWrc{ z_v`H`p>WR$k5xSlw~N)Of7iV-NVL{jSu$6au9}G)08; zl8|Lqp{~^l&CV;gr*hJ4NF_`bS#WU`MGpCsN7KE%04tPT=C$~q+6 z0H?Z7){>0tu|$>32rRsGptnMf%;b%fxJ|!*L!$F&ic6t4660kOdm=a*II2lk4ji3= zV?|MX9teXIs)ugdeDAz4~LB25Rm6DmiEBH!t+zIpsQ<)~%MF+9) zh*6*#cFHfrp8P<27DMOk(UACQh`cmZkb6qj2h>of&G~@D9>^_K_3s%%1~28+Y!u}v zCArD@TZpt(EJ+Oxdc|TdzO0C<5J!t7+lua|l}H6zZ;RM5&){@iNNcGBIKaD|HqRGG zBxA@B==X*6e<)ht1cMS_pj~B1^a~05!V8HCbp#<|s2DL?j+}_5Uy2hdA<7Tl6}$+Z z>FMC>w`wI@GY__+C2$mihZ#|LX-q>BiV}lNf=H-c?@U8C#E{VADiOv}roxIc1Kl-4 zXke5647V8n)Ob=6Jbg@le`CsT#3@J9+@8Yi_zj{nf9o`eeFV#hHPv*PP;gJ;gei3C zzVO&`HYRA35n)8$+X^_j%36MrNaUbRZ{r|yQVwTwFg}4Hwm+`Kv5f)hrZsE5M0Erc z$tLaxetV4>!n+o4sF&`TDT7#)jreWu|F|z~x$=*4b@q0VPZvXX32oEGh!XmWe#z=H z`T9Z{e-$g5q=^a!J@6S8EhVT~EzWX^TlE~hIHc}LWvdTDqT2TbR6x|_Xp&m=5wVn7 ze5<>sZnYRb)qWG=^~q^YvSyv~{?a3`iuw(urpSPcxX(_Jbg;d|xRx5D&;gEcnR%^C4al5%Yx;Ba_PM+^Q<#cxXzB zc#&H=F=8`g+fs=Hmkeiv{1#0xtMF|WJTom6N@O;VQ{&D zf4xicwm#`Ui5FoP-Qo z#*)bj;UFhyQn0+KY*|C8z2m`{)~7eNvab*C7EHyiOjjYLBHy-Ug{pfn-{O#rDZ1i} zgM*|fxgxN3px4sOV8G+(-a{KVMWu_&fA_7K$Bf=*K5%lK;*T<=R?vs|8tM6xv^6*; zovn5gI!fjVte|uPwjSeJ=?Ljkir|4fTjS%*_|%}H(z?lDlz1nYj(vQ9lziti3^;5x zK?5Y}V(t`rBpRv+mf8n{f8sE%L-7WK2HFHQ&w`pJm_9bBnt-Yv)a+MAsO@B+e|D+w zYkf&t=A}XO@|XeSZfXFuKZ*?!K)r4xkJZ^^wY99`S49~N4;uPeO(t#*8Wlc}3dWU+ z_Kk|j6|?#sEHPlb{n%sz7itZ zKm^ z&uxv!7TxNRZc*A&dR89Xl;x?jZY31G-(;eO+u4X|Mpiq}*BuIplFU&mI<(mxxTThc z5=X4x6vgf^9`DUTYWyzncew6yJL!mZ5?46W@OUjLJ8bMp zS+g4Fq>W@bc|a8Ao$iSjn=g6-QDqc;+%$}ghPTx^(Ain+eO^65D`nJ4Xk0pe!YCZ6 zsMqHw^2FN+BVR#)e+$MzQ322R+t0k3(uGM1AVBwM_Iz{;;EQ#~m9kzdW57O~KPv-7 zMACI~KZ26`Smf>AKe?^i5bb%CjdhYs?u?n*Ej6e~`aK$F53Wt(FB&$T;p} zlNxglYL1TY90(Yx-=_x=1R2`~&~5>&(Mp&BkOK4!&x5RvelpfUtI;NDrkmDCI(B~A z{L}Wk<%B)Mk??DO^ymcn4H`hybq0J(m8JaslGZ>lnEV?Dss;Xv2$0YF-QHy=%4Kj2 zm4ap`nx%4Ge>HiC0!sD^0n{?s98op2hpQ_l7BH|G*Cp^>>jvy~h_~w)X6ON#!mSEB zzQzuaozjA;_i3-~1WJ-ZcoSqe9lHkFRy#dU@a0M%G9u_%dj8_z_)4B?A3GWs+V3}Xs*e?fRbo(*(cygG}42?&X>O7#=H zUs9qID-7HUc`<(MFHrp{$BMCV3#9)VpSXn?{>s^4ttJq1+lYa%iiHl1USQ8_(14fW zfWZQHW)cix6@<^kVO@(Z@pCt!SIO*rpoP><$H@Gv5msN~kmbdq2ox#Lk%F{EyHTv~pWfvgm1=s_f=X(i;_C*lBa?hUJG?uI{b;zBw zzxqaV@RLG+Z9PZM-RB(!uSbeXjG$0V0`fEIsNXoMfLOe8md`*ddA<@KKqEM3maiwk z#VYE|mvI}IW(@Oe+IAp*p$4Ga9`)!Y@gBM{f2Y32#<^B(!Z8x>T^8B?4%!R`MKK!2 zemYmhZ{rw#Y>4x!Lhlid5DLySCT}hX-f&#sZe09EtJ)!u7PH^s{19q~hcUBzkO^Hz zLQXLdp|=7d*hAsVt8;YOQgkkZ^_4kO@i9u7)>5t)K5vQglb%^tfORgrjZ!Ms6`RbV ze|SY?>~hudaSnSFufl^J7lGY@U02`PrW_*>SDUhhD_X^^9Rf6f`E-%2hhrmv5JEYz zGVm-#8U~oWUi78fOdzDn+y>zqCHj#I#0p_4X(M!Cwkd^o-$f5Nr#&u}by;BN2ws}v zWjTPtdVvn`OVzf5x`;v0IEFG`EbeRpfAQRB$l~3WW%Uxiz;-FHABl4?fd-n|W`ACraznf7cPX zTjA8hKMlUp&$c7}fsdNuEb35|6XkcVjJv@Ws(3DiI|!|BM$V(MY^Z1!&5%k?L3MwJ z`kx=p|H>4}2?!s1h>x@JF{>P2V?Vw*GVB~!oWlnc-G(gvPIQ(M9nRNYmSR_+p0)W; z>*K1p?tuKK8od|oO*cWSaf43d3k`_MKYSr4P-{6$pxTPL}R-63lg`_?u0y2Y6s2; zQ^EDO)-3kwN$VA=JxGgP`+v0#v7jaPu17ss>*g*RM$y76JCf{7zebZ&_Vq!8whNVr zwMP{^?iR<45UVqCyoGFOf1<6D|58*>v_coPMAl?`nH44{C>9(V6j*7wP=m|T-Dqiy zE@O4F@0PGsz9Q_=G<1oZSZesU^kX@2U0&udfyE5e2VF|@BnzNqsg#I~N;SX(ZYK8_ zdeNQKmP=2DQbtimW|YCNo-dkU^FYeR`?$+3a#7q1o1IO~Ct`|7f3$+%t?#(&*q}{w zG1kLADs>>Xj*dEZPA}RQ@V!tQ1*-kN0MCY2$TzlJIFPxR9R(Khg}K~NhKY=CuxK{e+7wSKs9BADf^|YJh@WJ zjQZ^$R$sc}&YTA!3#K#gvosuWo6p{#I?z2@dN6{E$T`BjE=RK<(&8trtZ7~&1mQ+& zR%Za)^NSVG950`qI_SNC0WvNhfBJ^*&J01R=p{tfNHdf8YyV)d81}{SYkk>h?QSg2 zR#(jaj%RL}e>HUhT$a)^k^^*XFULL~Zwgx+9Ft8EpKK0C=Cr@RrP&ZdX zM_Tet!r~;p^hkRW21_WT5Xss)jx2ZtIO>@3rXk=Ye-MSkXq;rRh;0&f&@_~SUOG&; zoF?hw6~0=5xB-k|?xPY=XEIIRGa#>7{Ua^nxlKp#A4oUA+3yQazH*S%K;yJ+Qn|&k zD_`dlT2ivN9}-tAUBf)cYssooq6c(PYnY{b;%JSXJV0AiX3d_Pt&+m(U8Mf1j+-tOFV=+Sp-b#w%QWUB1%ldFztl z%DNMHbSgWaf6SodF`wU|fr_FmML$mH< zf3UcMucS*Uf zDlkP`jv}+#;Hw-YA?0SE-GiZxWfrTko1xWUfD{!px5YJnV9;u6s*=z00_z@lp?-L7 zWDk&tfqyUMUGYA<0*;7QxD9BdVxUC`e}dIrOfV!SGM_gvm=Gw6wl7h3R7QKriSm;D zz}sbKl7sMzOTM4_drL;MHQl5I2C(~h6oWeYJ;@kDtdB|gc^?4Crmd#~Rak*87kHlw-nqb-nDH{8zzO(sVCoF5f82&j zpy3o625RA1qG30Z8kWbWYprg$c$!!a)f7?Xe$np4KHhb}@=c8sYHrV{gnO)18Q_*G zH>T!TY7;+Z)4;xKO7Knzv;iEj5>se8aY9A}@GKeIlS_(yC_aqvgKZueK;>liwo75H z1jZ<)Y-%6?csBrSe6c2xQJX`nf3H(P^Hl)%|G|_!Gl2Z`$}7N5=tb=0vk@^xc&RxB zaDbeQf9$v{CL+~=-xXJ#xGba}RV>7lYQ4FVM&+GO;#C*^_{QwI1RK51P+C*_XSdtSiMvCN9c?nHjF+98;j)wsjr8Ze@eb73+MM4 z>3XAsIaARa(UdTek~kb*s?p?+5>#7Yrwz1iYPgB68?<>-qTU1$z5#pV&4#@LANtk0 zzeps%ApQ?x@R~4qR=V}ngaJt~kcv1bR$&!#X*C<-jtvOAU|Ov%8$lq43k*aTEN5N` z&?K>mY*d(K=pPO`d1_TFe|b=f%`IRhKjD4f?QMEfBlooCO!HM2`9SK_$E8@~DWR2| z(RpK#@dQ&O5V;@_m^(Tp4>)}gc&SzXC>uRVAoY*TrX0hDTA7MQ5C_EJ9C@rP;LU73 zvCj{Ca8ncEv>5sN$HO(fF{`THr}d)sf_B3-Kg7Jcw$kP}EvSW%f1^zpPE>!fRPs^D z>`D~MLLUte1J5R;1^dj>mu}5%y0?X{0jLC5*I*zAbH`>g*=TzND^yDe%S`GWn9I!5 zBSRIogoR%&M7;+tFx=v!))O*nz#n_{M=Yo;)r~~u1R(Kaq$=E@2_lT!Yd~uc`gM%2!@$n8}w0{K$ zy4+*LERo0-rdwyHgDq98Wk#ruu@b=WLjLb-}PmxZ59?9p6!whK>Uz3~42>D7Gdmkn6gLSg=o zZfJB6+|7P_YypGFD0wC4M`udg^bQZc!nqnkihypv?3Qlf%POUs`Yw3v7)*gnfVbRIM56#x%* z$+bhQnsKQg3IGis3#ek*uLHUrJ@^VNi5zlofS=Xy&XAyn-0$?9!RAI>c)!Z@(v-?2 zvJ-gAf3%(5X0n%_N$kwG570W7bqA$8@;T(IFOC6h?4GLGe2ckc&Ia9-pBLGDSvndg zl()Tznh)G6WY3TL7RZXE;Cai+I6@*E8`msJonH9Ts$kg(oMaT{| zy54t#q7km2=mCfe8u29O4hi+N9AM3oR9T(~nh3igljV_&Tk;vPxSIhobv>)f>m-@DJN!fN2-VwJ}x}jaGDqjn#!*K0dQY21$)JecEvzf%*Yj+ z;EI`H;cc-nHWsEd`=C5NMS$vbfBnE)L2(z;3_0_K1mj>c>aJwbKU1Kj=>Av$sHS1% zMnl_VVKf#s$BsVY!4iM^M=v2Qw@^qjGr}K8^nh_0Wn9Nw8Nd-<4eM)G(}QGgqtqdK z%;7g(_od_aedN(=Aq{2Neu!xj(eksSA_2Kn%C%PkL3o7^wok;j4pxDKf8RCO%QUzi zgO5`e_%M0}YKqjdIw3q7uE^l=jyz-`VADFH21xseDr4YNa&6acw40To_PE*Um5sV= z>@N17m&3xoxvosSQwjGIStC>rcw(Oh=_5ragHl#~0{eE<_lJaS7#Wg4oJ3cRql_Cf z^s!k6ah1>7tH4n@T)^NBf7$$Qk7u2x7a*yRZ9)KgNKI@$qcorbf;s`C%3#J~#Cc;T8 z+=1av8V=%c1@BEj1SH-$sTGYEAK6@lRSFvanH{;|l*^V}NMt3Ie}nLgbEH)toxXAl zw1Wn2?j;vQ8jRe|JM~7ZXX7+yt|cN}lq@)@wpgUqUlnkgB)hP12xiR|5s;c;FqS-csR2s6iu+Uurgi<)CO6s}!rNiq52#V2(kMPLE1c z6i+hpTsYz&_HZy~Q^UTBsr?0x2^j=c5TN`brTY?A#+Pd4kVEO2FL9pkxW}tR1*jtc zgnMul+M25k4InomHn@o2N_@q3{+jW6pafsF2<}#g2A+S8f4hcm{NNC`v?!JN(p=Mh zGU!hx(*9Cw!bd41I1J%&l3ofwP};iuR2lXk$b5Dj{R^XMi<=j+Gy&H#t{bE0ZcP{M zu|5DQnj9>4*>g?K73HhmEH*kcrL7&YcC2m?ZKN5F*yKo8bL0m)5(<#Yg1DKke-al?C^yY#FAY{zM^nmxuM3k`9_)yD$SuMXg)8+d3iN2onC2a~k8EljQrpmyOm|rF! z4|gXf`}@vV)`YK~mlA#f@3*tT$;|%EC!2Ie`6eYj2^CGnBDLms6N_%;XIH!DT#u3GY69y7RV;q*^)0-2q-oG3J_{$ zp&{i}Pq=_4xMpF^LW5P;>1!z;U*H8mMZf}32NXOd1f}LF6~QA&?Yqk z$#}C`=3K4m#Af%J)A&JubT=t)e2hk)I=7zLk=Lgf{(ry?3Z_kBNNEF_1j8Dv}X1!K|%1KrYmaK*R@Zj@g5NR;!JiI5WKO(#pHehfdGW5%eAnOwdE*nhgt zL$iR@i_kPk^~!CH{Nopct2%a5h{I4MBhQrgNK8Q&mLuWRh;j-CY!%m)U2GzE;ltl%#4Q4YQEJ@pDGtSw;Z&8{gufl zQ@No9C)4u;!w9Zw64v&s$OcH4ezKfQ`Y4!TP?F?=eC+9Ygitk;xn?u` z1HR)y(Um~+tyd6lN>Di((FjUN{O_-JfzzFkG2ln9AU9I(Wv3f^b%9#y1H~!YJa5H zx{+yl+&_&X3VWl1FzBQhoOvjb6uT9Q^IiNk>%?aS;c83iu~M3#ycAn$^G!=rVdfsR z(;kX!$k!?tN)WG)!paJKZfdpS-&+Pl>Vda6^0Mb+()55e{#5J@MDGk^m4ySzHsY!z z*r~^wAb;$eGxNJjwe?@u0kv)(R)5I3PM~z%#rp!k26gcrfevvPe{@u0!MO zThErQg^fFc#y?O0frEsM^O(_;E)gBs%A07Z%;b}`F^x|efdvY<<6!3I^5Y2ki}1yo z7_iHrL~BAHq7X4{NCI~%H*yyei%wC(2gbo&b~>x=24E`Y_&?-qezJLbKz}aq>?FN& z+XlVduLKUw#K{OE5O<-{0Ve)kyJnb|GSIboO^gaDNtsfTO1!BF5CA{`3Iv)65ecve zr=`aAlBAahgM4$llj38fk@28Rr6;6jSLx-+IT#rS*&8Qjz9TuLh%qD5b9DqOhWhEn z4RTwvN9e0`6f+8?5b2#Pis&>lG`8pyxmYMzbk1DL zRC<)ed^D6{sisbf{t=6%!CovhdzX;QlPTFP<&9x3t!wMc!j=6?NHv}Xk97dgqf=pJ z(TBwm*9a8%DmWN#-YTJBSgKr_hsLRkB^ut*6mQi2GtStGNRkzSXnzuQd}t%l(!*(G z#f`_uVQS@|xND4gQG;nE!`WeIoibyjup43txohmE6^sfR=``?4O3B?bB$ZV+gSTH& zlycy0)=w|T4w9kQo5IgGB$60;jPjvZ$350+B~&Ex7-gtt4KZUPB3mMe zaFj!)qI5;MD?#DIsed$h=>mrzZ{faSD?z2r=BA;CS|qA*d`8*EOY5l5POR9gf(6;H zn31ZvNqICf@Sv zklVXr7|r>=TjxWBv32hIcqS%|DMj z1P#5-UNnzZM~E`138&$(3%SP(pTDLRibg)+vGBKWagRqnPpc|5_CfohkgsbE z4V9#g^Fu_fxD|86RBP!UNfc{x;lwgK@U%RntTw5TlJzU)=4Mr^5?9R-KjTP@zv{SR zE_0!=UW#)}GJkYaN2+mzjBQ5{8LfDQ!WwBaF}AVVfs2G&(KvLg98wY|_!AO4sOf&t zY`juj2fgGpj*6lkY%Vkh{lhs?n!MQ0Jdj z5!1_5jHs=jkt#9|N)k>{RTO{4UfIKH4r)K!abAD9!m2r->Z-~!=MxT6nX#EyrNTT1 zL(8<>_R@#wL+cWxJ@4&4)ERDEtztu)PTKg)wglD^wF=vS&(o8D3D0M`%+O)s!tmyA+-S zHe9r+Y|&!x=b%WhE#-@^QY{rGMpi*l?f)8KXn*g(wV4Q{%jXNfA<-QiJEVdnqR&hf z(SEPG;QQ~XBH|_+`iK)7VSc)C#;L;cPgO~{vH0w%RpV2&ry5^5BqkBxxnST!e$z|a zJ;MtE000g4p&eMNig7n~3m?@Mn;JTh3X@cM z!+$T55G#^=b9hQM5@n<+nO8{VCPT`aPLr-d%7un_Zz{9S^kF*9Ur8m39&(zH9%Mxs z6LJR}dBtlaKW#;F5i22|DTQ_y1ONaQ%oHT!g+)m1D+#$!l^i1^ffG`Y6N&C%EeX*^ z5%RImQK=8z#4%b8m1V2A(0CMB9G3S3RzdBpmD7|K7X|e?u2v`Eypb~L1-d$SG;JW_&kc&k8m2DruZr~ zT3wBcU)NsJYCOadR#l@2Gp)O%4GsF9U<4&jU+5Od&_T>?sLUtW{-*Saq0E&foS&s+pOahO`LT z!rF{!SXmCYkq+_%Dm%7p9DhhG?V25^9FZzYW0N>BMci3p6Cc6EN`w=`UNOWjzbLj8 z4{WMQ7$P^cUDB4$g3A)O3_*O0mQn1x<8-!z`XJ=#@pg@`Z@F!`CphsSUw=Xlg*QhH7m~@h zhf_6^o6HS`Ar<7WHZT|--64!mdr#=#!9Nuf<(bVz%MVU2dRwO3Xtq+KU5wDd@jT_qEwlpxXE-5ofjWzsuhBvjbd z1P6<)+8x8n!$#^`Hh)REE8>nB2^}A=X+p_}?*#(|4|-FL!U#>ZgA9oa)+y+ZWZ`aU z;-=N&PtdEPDL+KecN9mc=v#sj`#d8v9f|lnBKf_q)QN9Sp_X|3w!}nvoBxZ~680HX zn>d}qwAN5Cs^X5tkm8*&M9quwR~4U(;GM#u+ ztj$90W@3Cl*NVzBJDi*5@Xtj^6XdT3IseSZ<&e#Z-XNbMz8Bn3w~Fd)@v6(XLe_=Q ziasdyxsi_-9Wn_+K9nk2=_QX) zRo59(VYvA0en<`^s*^CBh*YXuXC7_cbEf>Yyizf@)M7W%DY?P~i?cYsAVbhdr0Ncu zS&;wD)KHL6#Z26^s?b#85mcDzzbtgnaGSzrSs{Tn)zm9*Gp$prE1O<)0QUsEUVG=HCDvC(v@*P2P zL|KOFX4IG%-+M8YXf~uF#hd0)PRGzyup?AOj-MV1o#3AOs0W017sc00}bqfCx6Q-~tUkfB+JJU;_&-s0TXm zfeaWRC4vnoP=XL_FaiZ!5P}a}Py!BQ@Bj~#V1EN2xWEM&fZziZ;G859>capOY%qca zY)}Fd7{CG-h+qO6l)wZAm_>vVgdhV9Mvy@SF3{iu2#97Nf)r5Tf)H%*ff9V61R01R z0|{i{fD16#-~kt;Km;3Da6twj&_D)C@PQ2^padakKmiVTV3UxXoS8V1NGHabGmnaq z^?wWJc##eyhE#B1xy+V2C|TD{P%&Dw#y?RL^j^_f&1*mlG{E2l77znrL0uyj{6NSu zt*nBXkeIC&O7R_xE6^$7$(0FOzi&4ih7Sj-w+;vQq@@Yhfg5$COf$|Oj!Ez^*q7~3M1 z$W`V2>H9z_t$L^1j58NJ(1<6jf8G2bPt*Ff<_*96Y3`!haK; zpm>CeQ^A}bkUWS&V-d-4>3AGB6}*fih{Kxth$3t}A}=x}6XGYllt>^Uz9>C0Cbo?& zL0K&&)lYe1RXoWT_Wn?)rG|zT8U}l@u%<7OU!?d4rOx^%$dCY3LnXEj-dMTf%oq{k z_@PydDwO#nGW^8YdDs-nI-wTr9e+yrV(tpUdgK1uV@w~vI6-5%I>_Q+e&s)?A+`O7 zoUQ%nJYH&$$m6sQ-Q^x+q0&b*Rq;~1f+dR6Dnc`#eiNc(HI4Fwi{G=-lu@;>y0eHS z>$8_O4dL;2p{^wcMbWud%!_cDKhdJLk%_Xf9j91Bmy^XjB1z}qjbL2n4}Y(B7b(L! ztI`cB=}qBIq$#1ha?t-G^i?pJPkj^(=T(v-ml5QO1-tC0S!;NQ>k?HG$g`Q@d_)oG zbBPW}X;YDCjg-bbCc=84S^KI=pzW5DBj_FvSL$Md=KB7X`o-BrExhe(}g>By$!GLKaRX-?#V+(UJ!SU;A{4rsL~@vLSO zoy{6MBiMOKEc6Gg*Pqvnf=gslnzI<2Zi}gG8ar6gtmY01tB8GuPS3m``Gol)|f33O)>b6W)C3>M6VT>kZVS32H9`Pw=92=!oAJ1oQ;AeADx}te4HTKjqMT}2)Dk`jI zW}44y$}>9jxqpkXbH*`~&3u&^Ik^`ym(^)mM@I|^W0aR>!dK$p%F=G+86-XPGR4?L zL>i4zixieNDuK3=S}UBC-Vy$%FO$cG!nj$!p}e+{2T|mk%^9u8)%9ZuGP@uE060PG znUdGAoF^|%f^e>BRX&o>Jg=k?HMh`%Jz@%1g?bt__J53M9-ngO(_q-AJin0Vt?|$! zudNj737YwI<{qg)F`qh%lAJO{QhB#3`b*AP>y~a_#mlQ&!4i{lMh>GPilVBN$j{LT zg-i~aHsDl>5^>KhdC&+1dtmpMD&Nw+r?6h(KGZp2c$Y7C?C#vNLgxV@falG>-{ z=Y%Miit<2K5A}Kj(>0{Qjp8fWr0Z6S7StdNj#=R=F29Y z{7yk7A;O>+txiE!{`noUgKuU>?gqcz{U?Y;IGs6$ru1`(mW@98KFUK&?T9Hc;@e8D z9PH9qDERU-M2UHl6&>^6M3X>Y$zE!LoklQ7s>p~LeYYB1ToedLZH=_4`{`?gAraO93Ls()OU z&(Jrdn?554d`V2vKho(0k#5s$>4fGnF{xN&&hk{WCTj{RlMJ0^7BP`9BDzA~9#NZV zRV!wvh@J@cXPAzHy;#a4Hc>0=%i^TPo}y%rh1qc&voxR9E?L|&%{md-nJO$m+fs#NX_&6w8Lr;MZL zL+CDPf|O8{riQ+y6=526vV3xoYL-Y ziM9h05s}BLU>=N!$jsCnqyhmPyd=V%LLw&X@cT}Us}MGZeAsk;>4P?+o)`{fa%7_* z(tH#VLuQDGJYP-XAR+QcoPQAcQxj>(WZFCj-xw)*BRL%rC9Z6c?3H}U5KT#HrWjPd z)Cd$ubwjP}t)rqtHrwgyM^PyCfUUR1&FtFkT%-gEJ)W}UL2gSTX@htw4@u8g$P)|k zD=jM#ObJp&R(h(Zm{h4;C}gv)^g;?%=R7^BE7X^#O^%7Et3BY5gnu+m5}n%IU-Eg= zY{+D(Bu>x6P&OJ7+rCT{HaQhkb+Hb!u4r9ukTl3aZi!mrAQBygXwe)l{|K_|t#hb4 zm9Drb^D|*oF;A38JnOlLN3~J4nsF7Cg`#X1RmCuhYADJhLMbUzjVe>Y39@K-qIk@= zW~9_0zL+gCArK%uAb++iUgmm84ngvo?Ln(0wbdy{RhKEMRDJ|Q zB&by(jv!XKuSVQ6LLnrA2M&q9pRA@h|7j}8nU{#Il8}=#QeDIriZH1Py`KkNqS~UU z9F!sXJgfqL`{_{p>nF)iuW3kDk!8hc?-8?mu!nz77^;E zIAYl?YZ`e+%8t-Ga;n1Nv_|*ZdpRyfY%mSG4V6!GU>M{pf5h+-C|I*^^=AayXC;r|l8J&}V7idMH%?4BbK9f;5+ zpVC2vqU83l92J+V%xRTR7NK-NQB%1eVzQ`;Mt?~D38Vv)hd~Dv<5-(4nkYLMGjk|^ z>`~POqJxRf+d&mW%mZS@u-0H7RFt?(K@-M2sEZ+dz>!23S`Vj1^%bqbfVOanbuc2T zIEQPHIS}$Ha!?fer%3LG6LN_tO%NwRa*>j8x3Q$X-F@5ZN$8R#6}u zY=0;U9Qi@yV5NziY;wr7>0lgdYj#ktppnlVye>g5mS1-u-r15I*fce&PRY|?*h-rs zd)1G-ifC{e+J>kDsyIc;u95KQ0r|kQ2DKs~iq->?X;yuaXNp54ai$YAs)Pt@4;Vp4 zN;Q3aLdk^c$<)||W0Nlp)Ps?{O#VAQNq=^dWPw(0O`=mK>cM$c-$CM^Idc{D9>Wjnq&nh9qT2 z>r`P8P5gwZXd`kg+!Lx~8i}wrdLUG+Mg$QqG;I7(6jD;g@PI6)A&;&=G&J@>j(>*Q zL6IwhY)Ttq2QIeZ5N#M5AulfBi@r+89VHZT@feSA#L5KE=08rckSoIF5;7ay67tk0 z6dODToNs6c6T6|EYjHykhzC^c*hHCcQ5Q*&o)Z;{rW_^D1Gb^&jXLd z*;H~EJ5L+OA#(x9(Ow2vB5W{g8r&Nhxj%Or`n7gLlJ*|Y88ljuQ zLShQ0gYt|>&lH9l8lnf3xo{|Go?B;mFfL}A(D6jAh>D>+Xj`_z9MGc1cd&Rc=THcX zCrFTDp|B{Tk*b&6lhV`^*qR7Y|G*BB3(xsX-4qjK^)2I?9&j~6wD1FOWPc*w@8H4d z@A?~d(3)|T9Q>wl2mggc4dc&EDV!?oU`m3&lmwXrKf;!IZwetp5;%_fRy|>_ND7`6 zJqO`c%fVn+MLl>J_KKSWa&gE!$yB~Ls|31)#)ZoXKWTIiy&O>sKaDglOMFK|oyD(} z=hX0X;+0=4`;#G-DL!>MVt=Kf%aCzG&uSgrLSB{@BdM}fsjAH6RQ45Qs)mYV) zXGJ4+Mw%+ulungdcMY9LsN#&4ESnV$nOIdrz=4=tl+k4-5h_>#2PR^0=xF{Jya^#` zS|N*O7DE!v^^vAb<7r0h*j_HgwSs&k)547+$DA$VxIq|2G~~f%p?@MyUo&FCI;9?n zSmI>qOQ=RR6<3YtK&T{QtFx>{1=YMnIIIv=ERDl(7$YhnkiIzy%|K7)ki2OVr;!s$Bd zX0=tU?2&Nt>tKYGiU#??D0WT7kkpB2Czq5K5o0nx6=Z^p41bY{xbUg9hK8iXaa=U7 zqR(7BD^^iS$^5FQv_-3C)2S9oP*sOQC}l>6s7j=Qs)B-}f~qKLma67KK~zO2Vrx?4 z!bMWjT71f4p-+c2m%Yv5t-VS&^@&N_2WdNvB>i% z6K6d+Y@Xh(=%1QcWmF11gr*4b<&)5S;jU#=8h!lCh<_dmO>@qO4oKqR1PRNd7qy8U z^yp)dAAKZWLLPneKSf?VUxgu&+*B;&Zki)D9Fm$zY0&nW`>tA!2FSDj}@xno@SN*(vHYYJWiz<+G542AR(g9 zDI6IhS_H+AlpSq6JioO`C8SjvIu#v?B{V-#xPP>p!U|1;7cR8RBj)7D^iFjfVDsO5aWI4mBdf zr}lJ6TFmxNF(tfHHt`LWAWNm5@4XyY%GV+lK2Zms5h9yG_WOw95v)(d>f{p72huu9 zR@G2+Ff~1)ct{;gS&lj!b9uWlFPbZC}UvWBH%sT!)v7EKkkNLW`%D7LC$WLv>jO{bZLu4%efqAXFYyL#)9l5{95u9m0c^5wHDzC^H?pK_wMiK@x3Btd?da)G49^l7FGj zl>3oqTAQJ=a8s_}LMN0CW>dY;=o~d`W`aVYkI+;|gRi3e5d@fkB+r4v{XRQACnEc= zbeRqoxfetY%zbHAHdW?M9zPh|K60X^~tj#l#;a zoA25U1zkvLLyoe-*LgzIp}b^5Vhx#`snya65lSmHO=U=kGgQvSpIi1&h2)|hxG$F_ z)?y;lZ_rzrBC~{Mj2aS|74t}9rQQ-b(pIIKsmOH?SVE;We)=}KHkai}KeuZ&0bl`t zjbd^-C!$^fA>WV+#5F&wd$2pJ)3egYScg zW~vHJlpJ&D6wh3TTx5r$HVVWd4HeB=^TJbp8RC&PiijMfiHm5;7g|RKS+1K#qS+?S zkx`_6zXMpP9E)Hvtm?e%=GmXQ%$Y4*@J6 zsF!tWRiz+nf91yF=b~`P4gM}h47NuWu#C#6ENVy-2CVW8>=J39%m^t&;5~+X++3Q2 z`R$TGH?D|O7y_R;HS7BoB7#rYDiZLL4LV@&HZTwoGd4QwmPr_Y>y=D>6&^5c?OuZZc!ElLK}{X1VTHjLku zD}+8jo6NWw_36XT+)p$eCU-Y+M zi;Ka0e^kn*J(O7q;wn&#dPK{*k$CU#Dy##s`-KlC}2rT(U1AH2#WM{A*C5 z)@SP)FK0B(bEvFgmQZOh3c@%%(>F8UElktbe?{tsADh3hc63x6J>miT#v2s)F|9Ew zR%@j+-H9zV96{qY%I;XJScoHa4IRtYO3A8@gilxtJi+|J9b#&x1gpYe+z%HQ+}0}5 z!;`R`#ifC@@6S7n6PZ>*c`0Ve=aTgr7(Z88ldvEwI@}A#xXL*fh_~A&M*QKX5~Bvn zf5YW;Jt|rL%cj962h{~#g#iFM>H(n@=iq=2_H{^R(+>A4L?B&}XvX5D4P35ej_DPs z?3&PYv$6}U__r=us4%_WE}}cM0)D##N$or~5fL-D4A_xplA7ro2|Y$b_$2r7FoT)Y z+8jDl6kbt97G)iut=X_YgqGa~7A@80e_heD?_@LJXaiX|LzKP@Os&^8AJFDebt(}sK|cxiv-!n2W7$6%QD6)w z#4omF3T}UI35;XNYpF84{xoZhDH!82Vl}CP>hyX~WmycVgHEhfB3wqFrAo5se@e^D z#8`Xq-=3A^VeZfYQKg3$vNO z1_zX8QfpgNzpDIwos`(ckjP@Te=0CO(_}Zqbnd>0kVO|zN(eB0>-3J_;==qJ<&or8 zfX7!uOdM{9Pnsd{9BTlT1kFfZ80rZsPb0p%lUdIkoSM4bZ)~wmZK->tmkt$t3wT;Q zx$+8MVIWeehB$V(&rf)r;w_WpilKJUUf)eO6G{C3&iY2NK>e(61CY}Lf1g$efn65U zvaj?d;?g}4X3ikVh72n)_eL$u@B+aPcpcn-e~LKl$UsKV7ws3wiF{_50LQT$zLgHv z8}z44exD^~6d~k|XO#VrSIK~I7eA-NPMTem7-VA~ArQ77h2{A@(T&X1#+MCpUB~)+ zOCqwuS2+gSK7#7QlEj)&f4NkWCxKd!Bf1@K+2$#na$`!{l$GrIR$MH&l)F8^8LFcI zs$seyx2ou}JNa}&XR$FP+lJ+`zqJ8aVlRg^lsq;pyD)#TUDznR1tP7Jk(faQ!n3v% zi+2JL^zF49{*NrDd3S&d)6E^!y-V)0!LKA-U}ekF?Q}PGFaoAn&LB69#_7#vP<8=rVFb{7lje$zo*-ZVgT-9nG8S$48Pv6&5@#p z3fANN+#uMK{l83`#0C^u*8tb3(y?wu)aNzoV$Yi5PsLKMe-t^%G=cvD|K?HG8nPb= zB4FgJsH@*aWL$jH?yI<*fE^1701!K_Rg0cT$y&8Jy3%azGVOioKTqVRQ3WsyhsN5% zP=-X-YQA>mSgcUUNnN{&z!CV@hPhka*^%HOlc?tJow&z>RGwSnWrPid_?&tzA1kQW zg_zy1d8mT~e~%H%&m*~3Pk;5=O{m}>?Cq>^=_HmM=3(M_7RyWQh1zO#kSSkrGiA-U zX!k233)?+cVy5dvD)1V%pn$vSR9}taRQ58xRKpESvYw?vA###{x^TJvl);wR#9)xn zE_qyPPBDT0DqV@b?@mfe-0}HPrLo{mS!H>St`)*Ee{s3%pzb>Op|3AlbxBqFs&pi2@ZAB z09y2>{=kt`1Zyynt2GV;94JfE+EFxopC3q^>~$N9GPfeUO?Aev9a+Sr41E{Lt7I26 z<4ruBf67v?kT3N)+m+Tw%u38Mxug%{k%9Gnz26Aopsbey0`=4Z*>mDP<@i`sUDU}^ zfkkZ-*5rx5(X8s>}qnz%9OD3DJ`3tw}Bu~vRfSGUyYd0f6=Zb+taMH5G0TCf3gG0!yh%Vuyn|8*K2lfrPj-1QMNJgjh)Js6TS@i}{5fsL~2E@?{ zV-K?6--SS14{K+QH$q#K%LF>4tgOz0dF-Fb0}3qzTmhHUzPPKPji93WFv-L{QKe1+ ze|{JiOOBdlDQay2vNte`hF|wU7&PW7Vjcz%2^3SHpx@C5Ff*M{zbB z0CX=UT#Q=V21EW8%j3r8q{XL|wW`2l?Gl|&`mD#Uk3r@HJAMJ@z@$i!Pl9^1sk92k zfD;lZ#H-Y(8zYNd;1HfL*`olXE-bD@xdvA0#K#}3q#0>sM{1jHO&O*!1X(aYf7-M$ zvA#Fdz(owuHZClMi*=x!y}5=%L?E}cPUL@;QUk!-4&KE8j}*)TRtF@Uac?+8MOBje`MpbQ`tpncNI*=oS%(H3PnE4l{Seft-WKcnX)&7 zBLExCp8=7jz=oxim?7gWEfGrX;MB`jqDne}hd1Af6J@ zwE`iQzx+@>OFV74%PW-0#vsFmPd9aq)V-4CtT(ID!RLF<2&<%vLuJbByRXJgck6olF>QuD;Z#rehhl z9Hf$1jbrA%ChUgY^7x^!B4qh~p9*8*Qb(ZNsu+);Uth8oiO zqrxMg{Q2e~9Whe9qkRL&;lUd%tT?6k0Ax{Ektwe%cqf<+BPgg3nN0*D%MrrU!*4*y zgz4SDhSHnhD&5euUxZE9(2RN#_*J>GzX-hK>X*mY=!FY!PnNOLYkfLS`Tk;P8x-Ac zROVsJhshrg>Unq*e@M8ZJ2`;lo4jZe`w5SdZhU0b zbGk%@G#`r91W$9Gcw2u8Y!!z3Jej+KCH{nt>d3pzg*4jTdd?5 z8!|ETSV53likqoNMWLz7469CLly#PG>HgjJA$Pl!zrj5}FxFgo3WU;&YYYAK+FUC_ ztzXccMWvlfe_>Ysm`a-*XMpHX2bW~amsyNOfVISr@=v4cKdMl)w4=|zpm7LPSgGFP z!OXG2t=a)m;0ex2-y{OwHDG(5=vId+0ZR&x1;_xe10*?X+X;&hZdkutG zAw?3WALY8qi--rRYuc6lq4;}p9$s;Q{YCmg7uY_^*vazF9qx7 zXZRF=FSh~e3vB`zTk_(TLS$sXx zf2TVOiVT#{hI)alSJMZ`?9kcsk8E*%!;nRHic!Z4r00j+GN??U=(CAWVVF(I%xs|h z0zZ|$drt;+izKMQ%zzh_1r}GcHMFR;4b?opbjLu68+#Uit3MGTbel?i)V3$uXsS?d zn5qk)PM;Ec>Roi5Q@j@MQ$D7<+83>Ee<*TLD5nM!p=C#Q^;}1LX^j5+)Zy|zDNmzXT zNHgI5qq0f7vEl8}Fg(&GYai z4Q#(J40~xv>mxBU_;Xo!lFX8bf6E`4NzJCZcYH3+0&OSj@m!|%gKlS@+eSPPCU1yy ze`VE-1Hr0|DRkY!fAFBw@ZB;Hge;3RE{2V_f zl7h1t7PZ}BMdwwF<3m~XLZ3fp11sMHegnh@X&L|`1&C{2DrdHF_srZOX3)wqB1bKJ8M(+P*|k z_Q`J|v$Kk@@)kA;x4;ppj%ea5)EVf0B6@ZBvm0xpVR^ z19|Qu^ws!W4Hgiev&ASbCtdUNixsP(iO`Y{g`f?z4sUD)r3XGS3b72M2n~_^B(@VP z(I}hgya_T;iKnyBZY$8;<(Tnca_t#5SvHwe9irMv$3z*q@A^$qNIH|$rMnauqRuqC z__)7B5jt&wK~iFXe+?*l!A!S>8iAr7l#s54%5IxHCXRXQNHQ>7NCi=_$w%KJxRURa zK7KHxuVJS7h#2nlJb4=89403_2ZYpwK&364m{F{Gq=rodFf-$Lb*jh29*&3_-Yg=J zgw}zJ?~~_$-F($G3SYJ3L5I6(uMsFw!@AOz&B+VkUl*9he^?Cyc5t?${zJ1!@RCyo zTP#HwG$crLH<%(4cLrD`cR{(e7=%e{?p~pB&Z552SW}*Cw z?3Np?su4B2i=ka+mmn-GPeOPUO8QY8%HB0jv?oP*C#w|+5@Sk|;P_!lP9+C`03*<# zj7~S4xyOYM63r+04t0@!F*@&H;n$*oZ@IDE)M9MKl!_2W3Q0K}dQWE-=@*$N2?gFrQGDIup(q@kvkh|U14$&Z>0k|3egXcT<5PT2j014F(w1^5H>1~}UBB2oSQ{+rkLEhk z69|z%&+0sIPnN|ubCNLbA=Vvje-gLUZR16OsAT_kDANEie&~^BDS(=R0t=l-D|(2J zGIuIWFBk(R65stWw2~M`m)_EZ3KmN_BvTC@0ZuYFkcBW1=qXSqHnx<#>c!^E9BFh*m0-C}x+=j`%YMJ>wq1$W4dyQF=#7a zhH)PxtSD=s{GcXv(2mLt6|?TO1fIdL3WX;1d2nyQU(ey;QFh2a=p&m}!77HS|z(`>fHCxM#?C9SZPL3!>UW^@2m%){q;7Xa5v6yNo`hdt8N+qQ){iqf{A zoGubAIw4P^UW>>1`ziJwUiQl#gzPA&#D}}krFT#vMlS__-Ou!|f8@F9(q?HxH&O|> zI-fsCd6Gn!4^VQXp?K*M6Xq>_;~#0JD}-l0vH?jgj4x!Yn??T{r;JKCahfJ8Kff|b zoh4k}@J1B!LO-@kKHyZtS%`2FbBn!ONOO#7Ssy~|Mcjxp^t^;3ruTDXm}K)@3HK*K zfoeoaYLNHzYf_U?f5uN+A!`!UDjK_~>XHq>ZRH8uOq3R|QGS)8kjHx@odAUnQR$D&^5m5!TAN^7(4Q^1 zxlpl5XT3I&mdl{RqrjhDN7;(VPUb2vQG0W+O*p<7y4xgef6l4&^6Rn30?t-P1(2)7 zatsuufhaLd1g@SJF!S_C6Bqu!TF0L z!<3yuT^G(He@Q+lZ`6qe6(AQJ?N=Tm0>59p^n<8|hl2@L-QXDN7RCRne=Z@_n?x3MJTXD~!=SzddYu>s ztv?7-?gOekE0dgr6mC=$=j~h8_NB3(8mmq~K$!p?^?|T?X}2H@ zn`V(np2YC`KZH(iM)e=r*fPAc5u2h=n`u(D@2n+x*)NoX5EE-W6?%A}Y{QZnue7!JqdH$y|%yJ1k9 zbBA(rL(Bpiz3|`wl5IY=%S>dlnnVph26jN3EEX)w`&!~BO(8&QGkL9;t`jzz@-}s% z5gx6#NFjBAA=|Z~A&ja%&Wj_kuJ_igEB4JFe={k#1GnMjPt&&hcC2~3q&JKmR2yrB~q{|&sIvg08FQ+ZW^rgse zp}l?3Q%DT0?-O;C%{kPGVg<(`f?0CM|Jy|@hRJ{?o9;0{FNOTlSY)@A$Ufdb7cW;G ze-CKJ8^ZLptGj%Xa|-6H`@yC;$@Vjp?8|<1MBxk(5+b(1=wpzXM%ownBs4Rx96(pI_ZE$b{UfRsJf zRio);f~4vPlSWoXj*YeAy$%aJ5jl=UC>_g|FQ2ql8a6+0G(v|QfebpwYoo8EQpfXe zAAqw+m?-JrBy72k@$|iM+WE2D%ma!ZNfV1UXtI93Tb)xYmUnDLH;#*rM3qq#f8N=& zG5{L(_L5}gGbJyG-M_}Erj684&;G%H)@k~ptd?Y7wUIld&A}WRMVLTF7J!Rm06N|! z=?S|5y-3NGZk(5~4AFR*qk{vnf9*38pZ+DGOlbd4Nob(Piv6G;P?<>8Y3wWjJwU?0 z*AkmN#Ee14aX?Q^JjqC%K`DwGpUW<=P; zXF)IEc?0-)*G%R-zzLNC#c(tSr+dFD`p=siR-Y9H%u8Rs)E%?Q$lek8gS^_iM2r|3u0ZzkG`+w3#L?aLWB)!Hh;T*Q<+DI*7w6FtbqonlPr!L!vg3_swlG?qMHL}@?Vfcmm$r^ z24UIAUgh0V>QKj((LGKsut<#5eZb58(_FCAy;;EEhD2J2bZ0X#X zC&VX4I5S6483Ps7t|xGX4WbB;M-(BQwdlXpK@9};cY$z$2+#6Lg_$XkCLTPhS?_lO zDxSInAyJGJ5Nb64^j?R%D3Ea|BkU=#Yn%<4ch^K-{kQ>iHv4&_L<#3Z~jM6v@mO_w=05T3R;#mh4 znknb_0+Q|B8FfCJOAbSzWz74kb>1-KwND6>@6%vqmGke6>3*0{{g_Qig1{u`-~r;R zOq;R;tK;p6UCdKwaZjF{#)2`G_$wk%ZwuaZ^EiqLDMG&%etriRTYtC|`I5D;3w~d} z4C#5=oa%)RXVa$Z*8ZZ%I_5Rj>Y&olK%PX5 zqS_^Iat52+=Wd9A*J8fC#kH&y$6@CuoP|AI(klz#*#~H4D7bB700wp%EFyz1g2hX+ zkV@*H_y5+O|l2OsRm4DRsz_N7cg{@cB5eMNcxW?y-61%R{lk(XU?7|?D`6>xV?XxDfqk14C z%iDCmOGoi%9Wf6~1p?%WREE#>F1^LiOAhN4mt5PPAs55Yx=>M=g{EACbCVIcDE1lY z&*)#hc;Bt@y9PU~!G!uxQ^bFjuGvcDfFa*n4=9#1!hfJvZ66<$;G4+1!bHeXeml%5 zztUVV6WvfU2Q9FdEcc2}=_N5k1H7xUP%bKnBCu{8bS-M?AOMY=Vn*tf7>bGNmIGz3 z&OvL#@%&5^XMMLco|C^&cTdJm2xR}z8c2>=15Wr5ac#?;>L!gTHmYcE_uK=$IzgZM z#@Qs#3V$>ho;sWLToRV7(;(p*Sx1;;4Hry^`0BNnw{I;pr5B$paT3a+!J*ATBqqhQ%6)FDMRWSt z=v^7@Fd3O&s;3&>wS;FgJw<(?Ln3glo2O(OJ%7W}lIjO|ouR0_oNQ>{Mrd@okeb7b zf?>fb*iE$>ufoBfMjHe=G;(BUB`xJ6yi)(5YEV9ERd`A*q03js3q9dDs(%x` zXj*I%xDN@j^D{t#_;GqJ(MrhVhIqJ1cYi_+g!idqm9QHc!cs|GJZDiE2F`C9LYZao zX`2GEcA-Km!@sRz4Va+##~D4_Hp__}ncn-cyz*>jbEUnB7BP3A;3vY~HR;EMuGGuB zRNCcpPviKe09_|FBIeN6O>Q$V16X`5!m#l$$%~J=FaUX7|Kb2>8ylQ?Z-3x=hksXm zsG~61Vdem60!8=T9t`wM87KE&lGk;E++F3agmJ;d*AN}sus=CYyHBHmhNoVdMt-;ktHFj=_OhJ64wTHAIY%>daKP=@(U=tmrpM(M*Qndv5A4Ie*j*@pVCP zOyuOEn6T(z9X{g76nY31y2X#`s2FVnvF_9t<|K9&My7$?oz!U+X##L#CQW@^rP{2A zem}GHN%RLZ%*4yHrG;*!feYKzfhc&!vU!gjfOYm@PK%BVgxye@4CJU_fIsMN@Lh0m zyLkrFZvq<5OFkS&+k_YwQGb8bh)k#=Fh?KugiJwImeN?sVeMvBl^ZQiD~gSNZ%--n zS;1Q!=aZ2~-?VoiR{Qk;CK@Sd(ZO49c_VO%fLbtnfTe{guE`TJO9W5-cyYmtp4O7f zHNEz?`Ipmn^Dc|QbNzSSjn-U3VRFVN5f-~Bcpukc%QKi-C1?ygHGdqv@~rI4xyX$4 z$KObwShp8O1);pav&^W5VxiZ5?z<^uqa0M<8$d#(a4Tt13W%PuDdc_cR)5-`lCgUK zCXeu)YdH+@Ot2>eNyjWd-1*``qh#Pd~k3pK14*1Q{~aV1a}+9M8c07ZcJ4 z)}?taF&_&@jJkQNM&ohET@WI%DEhBneQxiN^#Yd5CV?HfBSlbMcJ)N#22{6k;1w2o z+o2eg?5HN7{j7!$;~@T^=w>7<G~j3FL81LgJ`bK1+<&tfk^0#)!%gHuMN4At zk?b04eMrrXiyu^!A{)6ELGGueby1opPaEV8z?UB`J~!7w zOI?jMQFbfZ+8GP)p@I*N(NIa!M4| zN>NNYY?Ld1*biLL-?ZGfN$mTwI$}oZ6>+XBXiow1ru~k~#__u277$6=>`6 zKi6)O{lrraBvU@eHNd z+Vt6=cYmQPp&0uqJ=%TW;8<$ZKAh#kb0wxB0P7U_z-YO zp|Eu~p0}UPq&2gQ%Q86^m`fT?2GQf>MA)T+D1y#mwwR%D8iue{=@N6_!HiK)%j1Jq zx&s4x14mUf1El-F;8yfsoMN081@MesO*}tuc7NdaN}bf9_vv#C1ZmQb7dUw`V%_rx zd8+3Q-D4VX@6+jkq{dl*U7o;2-Sk0ycT*)zki37MA*QmoVL>hD^*t?*>`b4nRFqyk zGgz)oA<1eM(PXvc?p3J-D_~c8wn}Y(Ou@mhI5p#0wSPba;fbVZFxkYKCrG2~9jq{! z)PKp0r@5}6{+5Jx;0lcRJG4M;Oqx{GoNEBHE|$nYhEi1SDwpq2z~#D2_M3?vNiH6I zH6)26Y?8a++uEn6X4a9R9{t511R@#si;Z@7qdly)G&=R)#ZLvHh0X=?(5e82&J*e< zY%~<{`vg~^d+S?81ap5|M6g+6mPUyUX@8^IYh%J*$yaW)&4D}x<0Vs_z0^a;Vp8FS zTJA!Jxj4*(f%WOWG+uiIwSZnZvUWw*XdnjAE!)uon+@3rn&qxr2903Z;JJyZ=?%6& zRUyA6&8>#4FP0@TX%4qnp5FV>hR}%CDReeMj80P_?L93u+xLCuSv()^26(|2ntxCr z5{h5~@jCz}ggegXu*aLe6X1fV=z+IC0I*8O(zZlyF zfV>bzs@7D;3sOm6$R-QkPBD7(V}yMn=P{3TVCkB=3UK1A zn4i#BdAIPj{4pv5eWYNt2mSy%2-F|yD;0<$_JIC-5!Rn8o;#NIlwmGU)m~qPlC!Vt0dSn?(V$D`FR)jCft@1qtD&B2HSUKRD z8(oAc13kjT`d5)OdOHvEh4JVQ2Mq&ZTq}6!^dw%Kp;K9NfCGUj+l&%TB`F5}g0b<1 zA7H2=_iTa;EB;oIxw7zQRDU>S>3|;Ip)eqPb8ll^MHZKQPTCmSO%!{f^ivf<-D~EK zv|Zp}<0Fa!aRS;wh6D9L@*T$)hv=8cRZQ6(C5M7~r-cD*eoIm2j~!7fG!lIkCRoj1 z1WA&VM4v>UCb_5mzTKMrlERN>%40$=4p3f z$-W9sxMRPS#yD21=0S#M%U{_4a5njG=u}ux0yh)-u+GM~2zFxLm0?qe&>61GZ^Rp$ zWxG|+S_Sj^956s}dw*{1#FeUXixFXW1hMfp9BJro5+8Qh0Y|LM;{4ntaz z-RN)(gi{*WL=vlT0UF6X12SCT@9QGy1v-g~4Z`u%^4;6t=zp|APBa9+{b|qPPTCb- zd`?6tASHkRIuE8~gov21t-F%4P@R{E2N_qzh?ZZD4-5bIh?1yOwC$fW+%MXh8#ffq zqk*L>TXuZD*Wj5g7Z7WBzQ#miiqagcMdnWwl$vXfcyKxqUnHOxt#-&Hln5mlgo9+T z;=)y?%%*#3G;YS` z(_HQ3VTkxFH7UBV>~(QoQ+OtS#Q5tcSdo61YifQDRevShj2p>kVqbTaOcykOO{#w0$g8stFS=>6vvp7=Tud+-4h#zYAC<= zzyYe9d`##Yf=Za&pj|8SaYkXUz}1R-jSzYB{8Mg};?+M2(E9ryZ?vV9B8z=yyh^Vh z$^8sz4}SwFn+YPLnJ2{t*@+jQO*ynN(Ak<(I9*KksG<7w3sQ2(Hpav_VdAUtLVir( z*ZD@IoW*i{FL|5c_ck$+I!3pCwrhVAXWS$_W*1r3f! zLv&do7iq>{xD=D7wStw$enE++VW{J~TKJBTe-Woc48j zg@1Vg*1hr(yI87k_9Ne32@4S-zaJd+UW3#o%-@lQ8S*T>aMX#T*N_FImF-`E`*IjY zbDu^2suH0S{HLQ%9?};N(rrOn`Cvg-PQ{*fsvs;?Z&qy1C0v!+@_026v3(c^B%0XE zJ`HM3Vu+3$8pt1m+cCSYDJa%S+UuV#9Xkzb-u&M&8b_y88LFlS2`gjIUXNi2I454%g- zYo?y=7mQCfis*A?(er*C5)kn&iensKgId&CW{kz^YW8XN4T4a>d`9)k4hB`%6o275 z-%07tG6u+~?6{<;WNDU=yf0|G;OY7h!-A6`Y1RbR_|B6JOHXh=EdZ8ArJNPJ;jM)1 zKonqevbSMC#gr7BRb;N;z{xP)uC^J*-e%kiT=ZsO2!?eiTj1gZ;FTe}MlaNoUcR^l zX#mKu$;@jo0bQK34bAb&6A=17(t0;`Hfe(rj3#@j1J~4bqA$tGQU0QC{awG-G5pM*x*w5;Lt6y=TMSff`Nim7gTlX-)0JS zX=velTo1xdO_P*P8xB%gyfxSkj>Xw`cvC=690V+t^F4RsoW)M-!TUn!VkU<5ZqBM2 z05JEERQAaMVLS`GS3>Paaeo>^*J)IAmf=iFrc4YYyw%vTpbpw7Cit(mZ4n~I(?3qq zIg^hS!1GIH$XEmR>KnW`hVwlbCOc+KU%ehSxgpYGtYrgr*AQqDR-94<(S>3I^$iY~ z{4xLxbkLJXUao=>401cP5+Sh3^&hTL=)(c;_O0t1lod| z4bvAZD6aDR%@60jB_=1CrQK}dh0`pgAr`|`8?QX`DCrb6Qv;E5=a?a7oRMEPv?QZ2?PX2FXa529IN+;lk}V}FIrjU=F{giC3cyrL%n z0g)zE%c-YBsGT-QDrq(kGi!rED=LG4Vq(soMSYh+FQ$euv9wD8~?bO7932Otgdi+q{a59vR$?nM26Sl zK>IN*d4E$VapzZ!nlKmoD;-T(iQKersE>Tn8gCfy>n-@cOl9hO#RY6h2Si-Q=To3& z7f?|S4ivSwgBSUSgl7F5%C!qPg3TN{m-_IQI#k2Eq{#Q3?!^hng5QtuMkG+HFS^dM z=amt9G!`ky)$V?PIEwzM3(C%E#DF1m6hdhQfPa9qs|13@j2V6w2Ai}5tkRl|zbhXK z1Xq+Dj0dPs5ToXhIP3-lJf$krb?O@ldbzCbh*@?;GxIcRGn1_Dyp0!9wD(MW-EMM(g7 zf(;3nQ87}0gY8&02oj{ynC*X;Im$-@+b+I?5!*|VgOS^LQ}YM{=7X*+mAbzXy6(-v ze{rckQ-UBVq5E_YXSZr*=HR0GFhU4P{e8my7fRmOU^S4_d|8LAd}Rz>T6y4j@>gwQ>R=fYqkxA^{LAgXo3;BPCUI|385sH=}zdfPg@w3x8K2 zAZK*^8QY|G(%8jS+^+>iFyhMRXMoA&zNl6JRT$fHs1s4lf6L-MB zG?&!mEDxa|AFfEsq(m03N?X{-g@4o!SMR4B8z`=zUL51f+6FM{U=k`wCdJ4?6pX8n zh9OpsPvevdfauvVsmqCLAoI&&Qtv)olL#-0tF|nug$XE$m=yWLbuk^*iKzOTRel4O zVHjNZO1VGP1Xywj^k^9-_LL&-!eI!&-QWI|lraHd|7Ok9zd>m%>+yQwG=F=47XhP7 z`U2QY3u10Hk@{>pDQ#ov0NA#R;LK&SmV!P2HdobJeJyZ!%qAL_s~?k@u-D$-gcX~s zKP?%PYkuny(m~>PZMy=wzZLwe(z*Tusqg&7UG87`{1qMN$2SLw!7^rMiOtQiKKx%g6E8_+Z5Qe(B{Ph)!QE zSLZW0=wr38SNBvk9eQzvp={z2$ZN)IDg3TER+4Mu{cxNo;SIQwnc!0dn2O*TV+V_>V>?b zRn-32KC*u2{^dAveU%@8URi$l z+xoP>`&)4!v|s_kMStF<*MNhW`2JS`+=2bD;o!LxLldaDVV|j%g&*cuf1h~sf~Mx8 z_NWc?iv*aUcW=e}b$e()mLNX3Aho2+Dl9BXiVZ?}X1}JD5(0YlZ)0X?QHcFnAy`M6 zAw+OdadNsm85Puuxywbw$<(B==R!mjuB#X*z#iG($duHQ|9?AfsZsZLBKE6J>pzs> z(r~n*Q1$Kp4z_&xiooz_`Sqw@A__d`nYf66aWNa)=<~~obk9fqk^?kjzdVA~crUS& z|8e-swXCml^QxDD2{0yJ)y1RguzvHt#(({6d#I5;zd9#q8&5ilnL_D-b$w*JnIXwy8TCm$SV#)F+*eZhwf4Fk zUtfJH$(Mrv{d4YK>BGMt*Xtz6KY^at2vs>UUXnKD=XdGhMcJ}lihR;u>yqFm6QxTC zy(CoU67?hpE0=m8$>g}i*+mj2TA+RCTtL4=s1Q8&B!9q`-ZUt_pq=@`{EyEP;_M_X zOD)QE3Z-eg1wnFHSE~s~*Ln6Dj`|}TB*r!**f}WPluM03oWQK|F*G)-qJVJCMiqsi zi*~tbp-z*9{;^RiXrc~EhK+&3eDq}wwVyew4x*8 zf&4;|<$rM#G;FP@Nl;6)0DKgtb=4||adSDm zzprxZ_PKy_)YDDH)JY;3#81ePBw@Ft6Zr;NF)P%Isiy%I+)y6mW1svZUr%Z$$9h#O zM*hCa?m*MKQBWRn)!2h^SYu|9)k;mwjU*cvFMrkhz+2mz#Y;=w_}!Q;_J$smT|j8o`Rv&#m-ucMBwueqr-$_ON^h4ELQw|eUk;1M^Y`M)c#E6+dP z$SHLwkt41M@BfnhoVw?R|{^`P6QL@yCmTc%$(nd?NRyg9&)tS|L}yKLN@l$bUPd z_7%6OJ6sL{SKJZj;q16QqBe-i)&5@lx|wHZo{AJcc3mN1(tmvz zJ*F-Kw2v?sQSAWF>ij!s`OWep|My>C&Lz-ygpR7>}FJ5i9R_zMk=PHu3pND$d}lzc$||k7nbauZYSFfET}B zm`z`i&MpJUAM#mFSTo{tuYY#|lQK>FI=Kw`O0@>A4lt>Kehw%J?-C+P zWvq5}!UKACUB4~ij-swk-h<&Wy41p85Sjx47suE8Nk)1)u5uu9)_-uRA0ZpXm*xpb zv+yOGKBmBz8fByYZG*RCp1;7*2#^uBtOgteT?^V}8>@SLY-yE4tz$DIT4^0wCx$*> zC|}CZUZ}X0e(VJ)@d?bo$fBv1*s|6W#jBImp_*t2vSdDq?;1fW zNQbb5dWU!fOUU7H%73r)KtpeOC0H;Vbyp&wg#qYFF;Z9+u5@3*`D`VR7{YsKC65lC zBr8S7pxVGnT#th1w@U6G_}x_r?E=G@Dyhx^2?LQp8esWSsYpRRs=LvGkf9<=*d^*y z-yO$lk_2mSR*};fQ-e+>dP@~q2r4z+Vo6c(B{g9>jlxa~FnO26QeP4E zr50hEq4HQzKM_i<)I$-O@!D3W+&2SiCTsfg*wv%4%BemTm0B{suJ8N6sCLOyDMZq` zon8QhQTAQ)aLVwUiWNpR+5wmcXQ{ck zz<#A}8J6I&Qmdn~CY8Dy3KUc7_L(3fN{tRlvYphKh-l_et5QI^T2fB>wjn#f0fR0G_zwgYNOdch$UUV z1}@hgwE#wV7f;?Iw6g=Y06y}!hG|Ptt8;9mJHQ1dDFT=R##%%tl|YLHQ6z)F$9ae& z4gfDr44~)T%84o9i`~j12(U!mk_Q7Qo?8m$0r-_$+86+paZAwq-&wfzHTBQs+xo)( z@#eMy9)JHOZOdN6(lC{eO09B+8!$?YRATw!RKE7hks32{?(Y zI6UM|N05RC`=V2I3M~9?%BLk|-Z^tx)T(>0rj^HsyXBGdNVfB?asEPk{3K3jKKAo2 zfZdCv)n8l(F&Nme-XXGmRHqrd$uQJ2BwAS%)1~D$hmw`2&1wY=NZ;gu=f$`@*AHhM zLVp{;XYEkn^DHvHoa3E!(#q3aXOrL6Sq6!`$efi0k7I|kBw@x$x>+5l_-AWI*$`*G z#~2<@&B_9VTR^k^PH=9R%%TB2$T2J7_&trscn9B=pfRm_-sr3t;e$7<5mRJ#->+e; z3Ef;w7`~4?7X=eC;MP81o)Nd1W51kz?SBBtmvEi-^z>pT{Omf!ORz5+s_Y`!W0Npl zTJf+Svs`5QHKz?1LiB)zx4inTvzaY9KkHSi#WcaXfbS?@wJw|Gg#KJNSqez%LNJ!< z^AHvavi6Ex*m7kDLimX|lpGlQI$6R8U|6x{_u5d`}P_T94%@J8_Hl|1-Hkbe*_ z##(Q$o4>_843)${9Km-OYX?AEWEH2JUjaCdAldn$F#zAjZU{A~GK;a$2_eQ30JMwc z;aV2UMuwBOhsBzm!>@FY5m;p)9#r*m1Ux`<1jTs88Z4C*O{bV(ign6awu=IsBN$(? z))ertu2|89qK>Qo8v(RZtesB(N`J8`T1tAI*opEt|zMsuN)vIw+8FK@Y+@=l?(0^trGAwpB3>SHfrA(oTHd+xrD(;P9G* z>fT+XP(^hP4qRa=R{%sy!_M{|C>g9Q1b5$^7`Xg&?+#deU1LqqhJSX7Lw|P=zY?B_ zFgDTb(LmKNs%AfpSgAh(d9nDygU@4;4=ES8qUn+)k+<(Wn0Q~r%74zjH*iWzG14MCGE41bjIXk<=jS=2d% zxc$nuzy~TW;Cc|-^7e0xcGlKwlw!fCZ75l8QaePSwpw1ZwCxmdf)H%Cw@Gf&pxz&& zoq@`tEgNzZm`yt)MpH`6%d=J8YDq1i-AZGw^}-bot^X*u3ESf)XuOs)by8cL3uue@ z8|Gd~tAM35=xCFt7JnjXfs`Ek&=!83MO$2)>u9?tj=vxVXOmXRVJ+IW!A*o{OS-G= z&UpWTRojO0CNM*Lr*B(9jwzNJratfWD`M_fdJ8xwW>3-iPL}$HvoFP*{+lc2u_UHO zYsN_TtrxXY%vkLm#;0;A*;a1YD#{&s!NU5=*nHy)DK7*ZvwvZWxpLoGHU3!tVy<|c z;Uev8+-F?1zQPx?C!Qg|(=gXC28}#|xsK9(xd6imq%jg$UM_FHlT1|*U*Ei3HiKU- z%r`w~8UOG)sRM?*Od4qEa>I;Qki6V$ypYfLJ>`yIaE=yAX$aoM%L$rIP;UL>+kcQU zcCcl=28wFd$$xPm*w-Kqzwoy|MMpK{CY+d>E?(vdH!DVGZz%j8kMk+>qVOF<3Kvxn z6;2fHUE!#rT?$_fKW%ynCqsudFu1E09!+iv7j86Z{=p8R-F>RSCeVpGs&Go0lomQ^ zn#n5#xWlcCSg^vA@L0f5g;xxkla_E*MIX{}d#J@*!hhH7&?To3e)>C^Yzg6zl-_c1 zQKK1PrB%NRgNxOso1e{0FgO~zudG$WoPoiye?ED$*~wy1yPjd%^D?>_D#APk{nyAxZMZG=G&tJAX~ybN#uS52ASfTIdQAz{*@k z;{H!H#WpT%RqiK@C)^3=;yw+Hzr&Acm&cwWaPcPg`&c^TSOB zfq!Bskz!BVaEy^h@szJ=^RQCIU`;UyHCYqhXCF#im(742^@$Ka{TIj?rvc1Lhb_C{ z6(}0QL*7rD7u`iwrv*U0W}Au`ii3gOBO;(N3{P;_7AF88ROG4?ieuj=Jz#c^vI2f5 zrwAOa_pR>jHhuPMEJ5(GkMX2|sAx%#aDT3fF<-6Y*9~Q!#oEwuti#2!I0<{Ohh_@8 z#(5gdMl-(_WMAS5@%CGHO5UwdXOcS*Ws4*f0q5gu&Y+N~#h+@bf|Z`M~6WyZf?pfrnbX**BDz^CXcwn7V$Q2u!(=*itVrV@{YTdFvp~k_ht-9yUwW>KYvi_Fo))zn)9a$ z92i$rt@>!v;fLMhOB&U@Q%?Co002SmCr`NO*ZpK}?41y)KA)3{Nnn-hNI#B)!MnK| zPfKqQ&mKsG%7}a3xpCvlr_At;oPVX~m7;@@YGO7)`x7TE*;Ym)1Tve-#|odW@W%T4 z3e;p&0N^3Dbv%J_aJk9*IJRa*&*fLnIdQSlmKp1+&qf|(8DFHKTGgQk%QF(V%Bw5~; zML%hPYf7Yim*8E4PcI6i00y&c+Ah6(7n1iE#J9zwsd=qS6Bb#PSpv|psMjdjU885X z2tS&pafVl7OU2~@V={t+xqrZfg=_M(%PkNF)CkQ0Wz|TrMcPnWBGLYY!w`&ZL`DT+ z7V*y|!&dCR6N6lAjf>?><+eh{kuZdLV1~1c;6g}u$ZY`95NZVBROEE; z;^l#)O+W5IGZ``dLgdV3k(M;GYB+&hHgCxVgE`Os>Ks6L)_aPDx4VmmFs1-Dx%kUy-{gUG|(C|6(L!f(5G(Rf_OC~<1f44o_ecl zvZWG@T|Bj>$`4tNNDkU(>vq9_nMKrJpYVh%N3A>-u>FVQ;eQwYxj(T0kH3!Z-#qZJ zo4ec=%4ks(HPyZX-(_)-)>bppY(7`?$;F%_RIiorwxL~*^YH=XO4s5J;I(1ZN?WZ2eVXiF_$e#ct)kZ~Ty3;2t5+vJuGCHS z!nIw=r)rP36MswlTe9Oe`Va!s`$C7$Rm`6@ofWN}!{=3XyXo*zKbI-LO{1~PhAQb| zD&IDd!ZW!zff`1nsaKvL@;Jpf9XO*iqpLa++DU4KfIr~i>_EPVWh;J}6-NxEr8 zd?lAB0X-;Ps5)p%Xx$!WM`iPPj};^F4GohO-gXlrHM^@EN>!+~x^?LHWr;Lq^K<3V zk4jY4iw;j@N*MF2bSST^6IZ7`XuF$9x&Z)C+1s z)(e$BcYp2|NR%=K_Hl%YZ0G6uh`hXxKr#vz$4w!$(x%Hb!fnzB9-zl$yc^z(U}W4o z5x8`kd4pHH!q?qX0Pz$`|tuU8|{!lPI+ykzdXZ} z;h!t!aUo3bh>?npLhR-eJ_BIO5aJ`879F&HFn{f2&=gWusGto3lH-upIm4AYZB#mv)Dp}T^!r?2uG{Tht$A3+To?T`d#h+*rZqJ1kzD5uLy z$A33%T{yLoP{H3LX?i%(?U0n37vx5v_En@f!?nMhh;#MG6I!;3ZmZOYNcuWso{0c0 zt2xNOcBh&x+^eY6KFiViM_gp-Pac%YT<~?t!x_L?+V0|JuP9$cSlk;;!d3FzrZU$` zFB9qByy324o;BE)anRXUH>fq0+SZIL*?+>uVzn0eOQkrDF@eM6SaoMUj43LzCGCTF z-^Kk>`G=x}t~8aw6C426wMRNUz$S@AY^-091D@r)w;Um|7fQldwUz?Cfi-*8;T4L^ zT8rGtlysP_7G?$OK{eMcm`jn`P?27z@T(e}HkG{exg{y$3E&i3Tk1rdv=MsW$A5t! z5GnO203(3Qmtm0OMkE(3QAH$8pF>jPEW6hjNnn-?@;lnB+Gxd)t_&zK)D%@j-V-s= zF&IGtPFOKlOW5l5`tiWQt5Vrms3Dwg@oi&nGFLXE7fm9S@xk5tUnZ;g$%mK=GC^^i5;I+PIk!!Q$pJirP?Q@K;ZO&EPkcb(TGct zyu~W>#l2G)b)FP`7o4g3ym-rg2NYWrb$771U94XQnZNZ!pfDIa z8}R(74I-3+TIi~o2qHx=T}oiqn$Yz!xDm-dw>+&64nPQ?U2xxiwrGIJmRZsFh7ia# zTqSEuLA`*Z$exB2K^rQxK4`$_2W1bQTQV{0Cic3rt`&9(;b@JXP>;c7liiYG!6IS^6x#UI&tkJggEnX|J^_h&HQLV#qjBDNj`r#u_tjvmQ~HR~E!A zO&cx!y_Q3rB!3BmKcb!}@$y?C$#>TSs$~m}6~M~g-+@f4DdHyIlwn$oyS8N6n(>E8 z=6UBBcjQB7h-9QBCL#I#eYwXO>KkHIEHV+{5n3ND zJAKw^ikHEO@UyV@k{%Oh?)jolG+qq|G-io~_CoUOW74W_8Nvnk`c~)Fc;>NB1*)gU*k5pti5+OvpB@LbbBE?>;n`ofqMNJsXSkQ>zrC zI-A6ymVbW$SDK=gqW~W^)gpz!UTBl}ym!S+ysrjNdW0KCR_`N2j!y4OE`6&Ye?rF( z2noaTo$kRw0Q|Z=(Rsn3c#|_Gx+MQTaRsW{fsSw>7?Kjn6c1y{`tSwk;z@#pv|(W& z4%$H6C}aB5-|!4tt0q8uaTl+WZbz5fk0L5*;D16mK}b9_By3V4kv#AxgP!ml6HsGy zNaAStbJ7%-2tfD}JPX#r+9F#dJa1(I{xoNxAl7sf1Mdu=_{z(`Rh%zo{S~k1sMf~|k&s)~ zZ-3|(z8@6pC?6UDWa<>3qPOqfvq#-|CV;~<;Jih$0oeQ=L%KI!nb*Ccmacils?6Rw zj1HxDV)t)Ytv|8ER9xt8Oea!Iw#EVBr#{RLofnU>1OP6ls3Flzh6~`Fc)GlMLMNY| zLcY_dPNgA?R5TV48khF%$jqRhRr~i941ZXg6Q@LCDGmZGorSlH&2EDRvW88iaxI;0 zEF6Zz!QM+Qva3y$SY#ER?Wcs3yl#^unbYYC=BfjVgF}IlX4t4Se@WISBYX_41GjAg zumL@C;ebT5?Nw0cF?YUf)@6uo@H$ad8J1>wL&a{P7M{@!hk=&eW$ClCEx2)OOn=-! znn!hK;fxWSqT#RnMnI8HXdAn@Cvgob`dELf!*HN^^f-M3J(1q20= z1{!&EcFoLm(fw_l8l zNh%pu9SEO#iy#)&Muxj0zAY&R*nfr3V~hmF*+8Cg&~RDupkDO>tyg@MSe0n#>!j+T z@E_uKcj3y3iaf9g15*omt+yMm`j@BOd#+=MmNYMn+-8f`Btqdx2x_Zmp{gf2x)(qf zd<}7h*YY55k$YWgg`6S)k-G9m*T2H0A3N0cxWFbsuH1BynGOb%+od9|l;@`iK2oLz zC{wNpp~R{XbmQ6?mC7tEHh=CjES_ntNqCO`) z1EgLdn<*3nH)AvlMpRrRZQ8#ROP*&U(A6CBcUj=1kFPaUUs8)Fld1}u%yM^SkMk#@ zFU^BbHL>C#*K#{_b7fi8d}zMIjtLQMMnp*%2H{LP#U9>84NlDvgpLgsqTOI_T0wsZ?NJE&B!8Y(D>>=v;j{_yTNai zF!}JC@164fB}>Q!|9^ckG%boDuHcj_?TQsfXJ@;Ca$Me39isVL2XNXZQ8dB<0eAAt zkVP;zT`o2?Dr03IhCs;&r;8P4Z!5Pckom7`8d2QW*yvtoIuvt&=(o0g5ZkfJ7YTF9B!kr)Zc{h;&2{Fmu=4bw~JDe7+vv>912PTQE~H zeJH{WnSZ`ZwC-35DBc0FwJ{&up&g}G6!^4a$T;w!QH-GY(xd^mB#YN%?7sCY$zgb` z*>1PK)IFl)iFO~;vuQi6_;VsesTCjlO~s?d=nVh=NhqH~$z}Mfx`$wZ;dz8ZUlIHI zG5jF1BwFbHhFOL;e<4H`VE8`2FfyY_m*IoSTz^jW&0|Fv-Z6Ri-;i{`%DV9R>t1X@ zx3J>EI}xnidf^2UQOOp*LoCS9SM+m=h29sQ6WBT!=E9Gnno=;~=!M_TmYA;9gyMC$ zXc>w}EYaz8{M!3CfdFVgm%mQ{4&T;0zMw}J8TM_*ADv{xJ&>!8FYq9PZ^idJP52;2 zE-)mnTr7Wh4$H)EQ0;*^hcb+ob13N(A@g=;<#ahntUh`HVTdSNl(y z=kW4T-=j989Lk7S#N^P2rZi)%BmL6PbU;eB|MH*h8pvK5Ck^IV3AF7J9RA7j9=TMr#pM3d2xOJcJz#f&45Ea2RS3DTmv=vQiI+BXA;o zl$p4n~Kqy{Gv+K`l4H3{4Bu)PVE4R3$%m5H|P6*}0!Q8n~ygsG6Jx8YM=%p};b zv}G-s?5}Ou*I-D-iv|b(hE-4X0Qe2#1Zq_5!jc)KewQik_vk{xPVC&oRC}aMUby&y zfWBw{3-@sy{k*ovw?9H*+jy#Af zzM(VprIj&3tP5RY=c^zd(H?@Udbgz3)7FwPD6^_=21cBDG-Bhl(`yMN3hgj zMr;xMqJw=stlpRP_<}GEO#7_<9#MZsz2v8vX(Uy0=_E!Ei&DPBqnqFhMS!(4iAQ)~ zp$U()@^SQhd&{Hhw*=4bsP~H zXAQbmpQx;&0M(n~ELJ};hCK;7SX?9z9I$^V{@n}ueope~ zn$)5M_vCJre=#Qx{B!UfN{Q!kLmlBC4 zLT0;+7o4zbZvSMMV;PE^)+K~L0A$!5@-`$4mH4E5ZvoxpNb=~X0$r8a~R{J81s3E=wqK=fL#>oF9Pv_o=T=wDdHs;3<|JY|?lfam-x(v2t$&yv#g`jmP9{gb2P`-TE8>stCGhvVz zf`W}G`IGYoYwIIm7ohlQ6lETQeQQQ3fJ_Z#Ze(+Ga%Ev{4P|a*Z(?cd|A09d;4pPEhD7udZk>j&jlqs7u& z{H;C;eLq~WY#P`MJ8HuutbZ-~6 zYZPnS9L$wn)ys{A%Zhs|qR`XSg4A7bUR8jvt{}|}*o#yDJvR6FxaPp!KwaHk-2erB zb)_3uuG&raTZ=I_J_3gyoI%4No+2)NS1|W28A!P{PQ}K-g(SCLa+ET8@^^or?1Psj zo7oBY2d;KPbdS0J!-;rtj5lwJs#;fVAbYlPEUda^bjjuK9Ela;ChcMv=)^&=g{0e9 z$q^$~x9Q26Ntxr;tLH$Z`V^AGY!2kLiru9wE|$1)uYpQRe$ZZEnI!iN2f3h^O%z5m z;W>_soIs$FgDTbz-2`&%n{t1)9+s46!Ah^;d8|Nm6zd(UJn3#5Qsl>k3Sr&>1EP_ zbfz%l5~OB_=#Ny$zw3V_>2&ox1%{sYnE!YYM0FA2$iy50hy=*HlR-2De!71TDO{U< zwGo*=1<&GXv~HuMeo3Qj;rS2eng;aQCu>j757E$xjzi(lN0phf8X?p7Js>_myx*H} z1gSvEyuCko`ls3(|JvOL$Y^RE2Hig{T$s^}pDsDZ{32NTA_To32`#kX7m>~IV#jQN3@HAKFpWBa zH~=G1>A-dmjBvNSPf3+SbojK`K`^37y_FUt>=(3I8JH6-Mo32E{m*>OpaTo7A?D)j zAIbkd+r$_RHEbv$NMXw4WA%9asCMcwk25%CR1@)x zayLCj&@%BhN=1k1wa7oz(rJL1Mx)NKekHFoAH3%9ay{fRr#k`a$TZBX$W#kEe3~ID zzPyS|+dzMP(fI%fCIS9@Q!?2NhgnZ7C$}Lt-Pui_=P+-UK#3&W4M%acd}M<0j}v*- zQe6AZhMVG&T`(KgDw?PS;0HjVD2=9VL6`L2BKL@W@&dkSRs!N}=vxE>Vlm=bB_Blm(lKv%WMS@+d$bAfzMTZB zE%SfN?nd5F;Xo2cBRn=Wl@+fybmK{CTV$8W>j8<43W(Z^cBY0r0X_gXjBDtG;P73RlYmrD{n>YlHN?v|&Ym8Ye z3are3J$P3LLwxcAaMep0FjPTTz>PGdSEj1a>52Fm&Ww@6&v0MK5%5M8fO6FqDkg*2 z{u#ow9r)b91~445S+g1LOAM<6Fp-9);7g*05ChH}G2sEz@aTG|HT)KVSO*1SFmOHO zfWqSHfx-HH<9&iVok#!>`s{lN26TUEnd$?p`}47!&iX&7kv(L9<9`>U z_G{rrKi3S;lDwD;I{}Vm8RBx=T)3`3j0_!Yy1|+dh|knlhDKv~&>h@VR@a>2NllfUrVZ_sU@*r_sdq92un~U{ zB~<%&5BXE(&|VTX^dE_#m<)dpL(51vl$IzJ9>d5=!uSPC4aCO*@AeHX>Nl{90^>6y z6WP=okv()FUx5wVq(yf0&1CmC7DLdY-_^oeWB0@=A|&AcNY&i5T5u}USqxLna7zgV zqPtJINvz^{b;GC+-v1fl_?DZ+5Oj_MO6B(zzA-7*>}!*yyTKmAbaH?DR3;IX6M$}#VV9@0E8w4z4;-}sFY@>|nY z5oQF#!~!_67}<2s04z6lUr6-kLRQiHyI>JT`Y7w z?^As#^h}v^4Ag%yR<8FB@Wik~$vm)mD1ZM*y)z ze$Z${Sg!gsO0`!FIld6YO4~Bw3t6Cy)>b*JeP11i>56}v`j~i{L5kHF9lZ++woDgf zCHCgRIaCW27}T6R`IV!5cc|8EF1*-B9fmwO^X|eBNR_8)cj49f!buXFCltECaV~vD z%3en-{C^ilfy~Y3y$h=j-OOd2i%`~D-;!pba^~`@4!>M-#K-s;G$nZWxyMBl-z*05 z=~lFORUdyx#k1{zpeN$azO<+tdHPsFR$bZB-G!~(%Jn2xiTFGwHU|h1IT0O z&(>ZYHSp>M>1OQ77l+igM>Bj6KBD3p*U4NK3x4_HrVpp zu!?5}ac0U$yUj6j$fHHf7a1eaq@_nV-X0P3JqNQ|I4bfjWC6WVSfzQpuwU;aarvTw zhtn%*Yb|VRk@DiiEEEkPo@W-0lO9Lnb-d4TXiKEk!qWA0tP$9thp^SvJ!YYw3{5BZ z!SjFI$PB?;NDG}g@Z=Xn*76&?RSOG&SjPlMsFc5g+$~xQ7u6VevlgN+C6!wX$Jcmx z9Cws)AL*76uZ4K#AVCkHQ`f@Y-`;e#YSt`Ibgfgq6Ws1%*h1+`uE2z03#;Zugu46D z(3fKYA36>T71LEs819XjvrY{cR_2Us0r`IcgqAbFv#{d9<^8je5B_SA;#v4>J3s{u zLenJxY^%E$2XU|Uh3EGR`Vf$iJlt)9(|(!leW21K7WM+t_*rSUiG`y!A{e_u!Bw>Q z;`grx^Y`Iw!FI;}v5`r^%LK~K3MU39meB{`V47L^(nQJ26#f-*aFRHjQTT_5gv)=P zO%pD6^p}k%|2^)p0aY4Fa%-T zXWe7bwLjF+k}M2Kr+HQW8R*W5s9JI^noPtK;jbf2W#veUML3R}Rs^|p2v5yZq7Y^) z9EK#^xo5T%aO46*Lf@25EVjhkb)SEfxd3AwiO_?($%o^w?W7u=J6j|!2!BrrdUNm+ z4nx)&xnvKmW&kKMkXq0KU{Zsim(Lg%^~UdAwta@yn8t_svy)U?&@Xs&X-!@5G2oOBuIY@yI4n% zEZVY61ZhE*OMj6IS2!R@)2WEri(XZVd%Q>+6f_4CBqx^BMMGdR(?EJ8ln7qRz&L;K z1p|$=MJKUk?}qv-(5MRP`dRineu^#2<(#V~foWj9ArH&aGEVDXxhPs=?J8-b`!}Gu zicHU}95PUVUX^OOg)X4AIhKE;rgAeznaCSa*$U)*r@B*@++|9Xa?D(Qr_ABr1}&#iMr7ViKrIhx)s%dyin5t5 z6yubCiSGg6Te{I!f2AkK3snk3iKC%p#E!RslH3X1jF32C{1JzGN78>R+0;(OdF163 zpREb7j@-O0Oj#1^%YL3oNDpxdFOU%%mD*OQmWhklE{>)v3T3&2cCshxFb%jne&S(2 zzd8E_->c^5b;TBG%krCVGDP7DKUbte7!~64{P6IXp=07FmYgnw!Y}vE%SQ+J{c1;H zXb>n^NvBFslqSgC7id%MC%JHp^T`$9?>^QSUQx-XYH*$QafeXCCn)Yb0K$>SKZ7ztsHnX2l9W=Wl%xUz{KNf~J_cU~YX+CLu>s5# z4BFrPh?U0g-eoa_IUPocKh%$KLYJAc0bGAgku;9YvBWV)2ivj3WsDLYpW|F*QTh?O z?{F48joWufT50Oa%>4-KE+VEqhqFoBci1RPLb_D&nQck*uOG zF@&X|a)qX$>1XxPA{lPc3$cj%c_LECi&CUPUQ98E?pIb=nb251CLkyU>c zq9DrDq_*sHCeJvtv~VNm!164)&!tL9RnSyk%Z^%kr0gVgigQ}D&g4`Yoo=0$;xi?h6CX8xs<7jQ__tK&AB@HK zPL*%9@l`*ju2exa@l{u4ysDLOaq)k6UnND#_*82gZai3_*^szUg`$kR>JzD4C1Tvw zlEzo{UYc?Hr0rgpLMp5gcX203H=T&PxD&-)+`71nTSMW-r$aHNI#dm6{PRv05<#38 z`RSESOlj&(qgrw2{cTXWk4u)jVb3C|MlSpl%-2exiR-W>3=sW0*K6 zBBwl~VhL7U67#SGd5K!8*gf3XPHBj%^$D3M9jChD?sc`=_&6HJXJs2N386+!QHkS3 zrFgU#p}1wCXw2uj^P1Go)D)koA1^6wze}=}QnHpD31j3E;)S<|w9{`YDjwSBBDYVv zeWHu)lvYoVeW)eMV_$zeLWx)>5z%eaMt8ejOF>U5KE$a>g;=7c?sLzdyih7zVy!uL?7lX5F@_)>aAQpoT-d!@&V`O&T=irUNgsd2-oy4)4Em1tGNfql;c3of zALjcoUmidK2OuB-1~{N#0S73+016TyfEa{yF%%XV>U^S&S1TirFkdAhUeh(+8tZ}K z(SRM_FWW=|c?rwGgTuYTU(H^1O~&@sgPCZs45*4imM_6x2{~vLgU2sIyh|47L1W&? zupUsgBU67XJ3?OK;tp>i=aC-KseoXB0L_FBb%Mf-Cn(HJm~og{;8bF$F$_c$wcrZz zx1`6W-p@IXB0*cX6^$OQX-!17ZRLZYmu2m%G$9mXJ@MH9}@C zF$?XeS{FK0EQn^4S1RdZYBo{xsc;zXQS%MQV0nKv6H$Aat_kKI3{ndhhOK*!KpRV+ zT15CuK8ck`x=~Gg#;8_8Q7M`9R%{q<=`=wiTpC@v(xc|mDKVEmiM6J0vB^Wkf?l7Y zX~L(bNYYXG%xRzC&;O7v$lammbdbBE3=!L7RzNUNAUGuVM5_(bDJ>JG>oDfYygGU~ z&P#tj+%MWlX{4nV{VKIKMJv<}gq5786yhVlNGeQliZu53F*XtsK{KsTkk6P9sX~Z-iQ&4+ChcCnKn8EHMZ@`QJ!x*{Cjb&Q(GWFcl%uQG|)oEa3)rH#y1;fxi- z%ZH;iC?$(P*-K(-7G=5I^j4CF59=Ag3TzN|;TuCER7UTC^}tOCMno_cs(@gCm*TAf zB!7AkVI<5D!-x+=fyxi`KGZpG5Rt=BB9SM^B4d<`+L=qrQC}vhGOpV_D@I&Yv3;Bt z{Sa$Tj~Iixridn7Wx5%Q;-0R^FU8b!ov1?rU%e4x&+PMz8el#=OPiN?%8%X#Fxi)|Gp77M7XuH64q(20!cHy|w7XWzrAG8XD28 z<#29AVjYR^!BRB72g~_id$6Q^s=_FGR#P|HldCvtXJ1Pzi)qEqRWh-aeQs?P(tp+V z`<7D5smyey6rbr!@tMv;)8n;vr#m#==TckUgNWp*hU-_g0B2FR<|(F;!wzwZq&*Es zi<+CTJMqh89c0%i#medaw2(tJ$@!`#DHhV4iC8nrq4`nDyShYV=Eum`g*jf;#ad6o zMe(EcF&EFt=_z#4)Q2|{UpOkaDu43|Wu~8yM3NcLJUkWl7naXx5GNwi2Pyy9BlMiq zmr35@iW&Nx@<#gAoN5+%L4PKRD#SF>G}X{V_Z)LZvTLN!50<1hN9vNEn8teJ_(8UO zqY{mc)I6ibGrCV|{G+uJ)!DtRkjyU84^ie(oTel3QYYT&7?lZUOpa(e(|-|~5E_?4 zgw@aEB>kinuW5(dW%Qv(>ZQ)1@^zVse;Om2u3mXZIc1(s`egc?a#t|=A+BG|kd@>_iG;#G}!-@WmuUTh9gT&9mYZMQgVdo|gs zl0zIbaZjYM_iE=?OD1t^Op97HGGyr&X?lHGuslT_(~TEa*4cQj(ti<--?|Cz#MU|T zDq7r~?ugow$hLjbj>NVZREpB=N>ld})DYK%RN_^?EvyfQRN_Ki zRAp5eEsLtSsi&2zggGPILprxR?c6d)jdD91x|DB+oSF??7UguZ_Odnm*^!cIL|Mvc z9r7Z+MzV2VQNE%Qb)_HwXsv9Q1Hb_ye`CO+if`nrp%K-H8$)l<=0({|r>Hf*ruLFW zO6ZCVztMzzf=I7pH})7S>Ave$OVFz?{x|E(+1iDs*U@P$eK-9XjLVv zj_Op=&asg9Sn{kkHDYX{-bA)fRi-+Rld`UjY|hHYRb@4)Mi#AJve}S1f5mT8a?d&w zsTc~$kVwTW)~qDxQfV#1+_<&Z>fa~c)aaa{7}I7Xv6e=(g%8Q=j_+u=S+|en3_}!x z=m%mTF;yt32#qfpOqj?)W;&tMaRpk{N8E};ilGtw8hVg=*$sRF!2ki`S}j*-NmT{c z>wE>$#8kNW57AU0Q!9ZJf0j$QNVu%R85;PCaWC|LQup(&maLywkhJ{J78JfJ{n1&P)f8|qrC3c}|ncaCB zQpIK!^w7{oLA%$Wv!eCH4{!ENN_oYksmJh(rVnAu5*KHQ7SV)>gp@u;k&v3i^=wb! zXknNloGQ&;f(~-c&f*A}i25jL50b{FA=c@|64$HpLcAbPWKpe6%E(s)85_TdV`9j^ z&SW6_I*fH93)c?Xf3PtwgKIQLNQ6V?(w-AGp@wK5ZNo9!d2nps*`13=-ix$xjmRl9 z5ti+SYjj&A=CW|)(ysX)r{e@`2Dw*kmw06s&6jwPf_w>5f;i)sDD`23_gUXJOFuK0 zN~ruY_&ZoJxfZJhO(xX0ibZ)B8NVAZk0B?XTO!7fj>D6JfBsn?gQfCo_D80ng3|Ge zs4rtsNuG`IMCDSe=`t4iNP}rn7r8?iOpBO^7M^)me6ME*&3s%Q> zwMr3uHO3XDt4optF(_;y=n9poJX9<)3>F59)M861e@w0Bixu6b;9{J^UP@rmHew(V zi=h;TWGzrB1Ox`ecD-W`bvehm_z69tH||^$25nJcx@yEdGMH4|R3nei*8%Fqcc1{}Sfs z$MD)Kf5>ktl7!die^6Hw8q?wvv06lFVx-xRDLitJ@MC&JjQrcNL&QGQg)3!(;?ry> z<`5fclWm@yFGZ+n*f4BnJk>x{4y#Z^3Nj8doT74&nIIEnESF-1p;2DbC`j%SHMl)U zEv}uEKzVxNTN7lS2(Bo;k8l7>l>RP zH%&N5!b4FLy`DB7Dk)1ka_*BYFG>u|^j2Ual8p?Xk z1TX8zqsQtu+8fy~KT&0>Ms7#L*73FmiVMo@gf5fmel<=tKaEOSesI{;-lW9k$j0U}Xh@bf@ z^j{WXD#cWsAEbr!sm?(3g<}{)GfMc0h)Bi9+=h-uk&08S;)O$w5&HDm)}<7V2r0uj zw^D_CNhun2Tn^n6q!}lRB%kDjbV5GDLwOhDXzZ;HMZArMF_B(^E0=!G?nJn8e>8V< z-OX)|S<0^JL94nm&elr9eBB+r87YskMu}9M2`?qRMYbjL;Dief>m;if_%&=_14l61ZrB<|}(~ydmSkdSg6zbP5{29l&XAM)i zVnlCY70I0~2@)ytG*Tw|>QpBhHIe;>lyxj} za7Ye%4Pw>2igNILJ63{7^XF744GM`QE;@^-kn|Qd9yRsuT&y7uiAaf5c;pw6;>>ts zJ>!VOk+N%~U0T9Wf2-mV5vr{9Rc1WJ({AKqB>O!{Pv%rfi3;Ck$m+6uNfX>Ho@b)W z5K$2w;-Op3QLK{8vQ?!SLO2Z+PIogX!UzT#T_U9|4ogWz3=VBh$T(7ZR0Ovu z$MLVV9z@ej5Tz3gM=@w>hGsm@X7l*0Q#+~%mL=HVG4`AJe~K=4WS=LVIG*U9v;=38 zqV&=Fhowd~ib5N9+&+rOYlUhnH>C5e+>UTUkqQ&XiE#4b?2as6W&BJs%Q|haTc={v zsi-McZ2CA0tG4MDVgk~xTq?(0+*=jqIHGt0X5 z{1}o7q8Xp1>0!RuGQ04!%E6Iwqf)BYBHr@fQdA0dNU0hLvFMo)sphFti!#NzC{t3d zbT}P<7o1jjM6U(MpkEBu2nlBkAqgaF%%ID%$El$oRgz*JOww%Xnyg64Tv9lka+D5o z5E6x$7R8(Nx8@%)Syp~>KXfWhqz+3WF0Jv!UFYSu#=gp#L{)@*+vu%9PRQk(MA3rc zj2K)_*0_~Siban@79De`njq5DWq-7IHX7r9n`vhnf1?Do2&fcOsKkpFvH<`9^Aga& z7z_jhf&l@s>}Y)yfDq?uB$|oga99itWl1O%696P60672v00ILdH}f;_Qnw7|3qZZX zXKc{Y@XBAlbRxWfjeyr=30Ie%3y?KFdE!0qOWp!1G(@EN9Am3mfbdo|(gNU1W&taI z9*9(?lk@Vhfc>6JKpsZXhsAXT_?6@xD*&1wQBfY_r~*zh9e-h~J_SVXxaBw@(&-C! zJR8NNQ9wPN0Cgln1_ex)l;{cApYLZen@glj8z+Esqh8eQ0a9#nj~<~qfooo}U#jQ# zOh7{*6-zk#%rN660DCfUw5B5Zs4cSDD1FDID>w7=w@Ns5Xhk&g5 zNChCjYf=cHz_O@_o8FWqbvR$fuWam5MV@H?lKiX%0W%>GXJWb(1PGQ00|ab;{?wqz zr6}}S2>bvDyN2-l08W>nSB9`F3U&|hpQ+%X0A4l%|5d z!{q_u46+Jv2MD2XhQwpBI7|nSyn^(JJ(t48xaUea0IX|UA-TNqi3|>a7an;22*3gS z4*&4q06R(1ps%K1a&)xsa!KWPA_KU%&;>ETOzVDG!z}n00F|$Q9mxj!6}epinRrP|X4zm&sH5B- z3Ia~UAV!{EglN7aM(@;UZQeqmHXZZTqhta8OKCo3+xZo{D$M}U)kKPaZj*sk)_Wl> zb}NAHp%*VctqKnK90814E#z|;O#9@9OA0{Jm5zUO5`BRJpo>DH+yvlw2s-b2dAz2& z1Q3A~2v1z*S7R402>`IsNnEta5r9}Kz-8BT+e82_BV2EZ5}aNkz`MOk7W}NW2nGQG zn!xwjX#mRt=*I`(I=Oy-DDs=epLgE@q_DtsD8%GIGcX4rzSE&;F9Q)r(JnR;_D1Jw z0E1|B{$kC!+rN$g zFw(h&BzJatxp#GcGe-c(OHzFn-x2Y0Y>fbH!^2CbmDZ}9>5p!5+X#RP(b*Ce@bfcS zZCRiZ00zQ_)y>pwBY@>Jzu&&evBL;}%za_7lF%Svpi01KfZWV!DdJ+gil{^MS$>T& z0`Mi`H^lvbMn(W`e+z0mr1vhtDeQFhe~}PGH0VEIP?11?FNCY1b&LS$oVI$|As6|h z7y%&k&t{{yO+OK&e3B8sCqf>Ec3SLjSzE+=@`Mq99ss?mC`JI4#?j$B7!c10;P^K@ zrv5;&Z{)DgyfFeuXiyfRwh2R@s6|7%hlJ9_3`igO{sr*NkOVF#wscgIv(LN^o0$pEYN~}cy-+hh%?2!x*!8WU2KKm>q@TAqIp z1Q$Yo1P}}(QennjBvA`7UE7dl6a!im_p==`pdbLrL`a0nTF6TTuzycMv}O`@bysAQ zIW$d_e)*e!%Om=E}1a1cfSIuXLLS7ZJTiFt^ z9r=_s!~uvo=#5X%07zpCdIn%Cbe0)_8&IiW8GssBB^`JffMyZCidb?&OzilR0utW@ z#fSjBleVHZ8T+BSpo8vqr#Ay|L5$L#jD3`Nxg0Bj&<9Y{^i0Bo%Q=I(!gVkL%ei8BCd);U9nagcRpQS4~-GXQdvIL3}P zGNlZFKM+Ew8nPtLHrL8rWO6n%>F6gX4%TyqeCc)Wy%(2J_yD#&8G!kxz1WyXC<@$n zXa_@W$!%;hQPB&hCj)?{n-W34kO5FkUGmzNb$crG<6D!aSbfM2lmS4`$%EN{VOs&& ze8D*^N@y8?FtzMf9F!nWNwe8*jSRq{6UAl+Sp@LI9R!O#mI2spByfJl$uaMCqOOiM1jj0+&;CRsFXj_>mHo&gArtkD|cJ0F|EC61DR&j9eBGe+O* zxAx~zr1bIb=rN&hs4gzcgrkOPwfKc8paBmfVU7ab=0OTqd4Kr~XfHZza_Dnk2obmvM9eB#V5Fcyz z)FYLg2B1nPL@S*vaAjF&Y=#!z`x3NlMjMm{(*Q*4i#?Jnb1Y2*unYn4TqB@Obvsf< z0F@@!0KCAr&DfY?cC7&zHEO|f-}81zR1E+|5`R2@nl0T+E4JItJez&H zLO>0`%9}aHiI^y=a&iKsq>`Z30Jzec6jlJ~Yu3G)pc`g0e$!v(a59Zz0B|GLpvkc8 z{A2-;!vb&=u~Hg3MY;kJ$_h^aFeS|m!aAbFau@-?6N4fEtIQ>B2PeX5m&pW(3LXGA zi6JeT-v(N31^^a+Vo~yLtLG{JY)xp`e^IK>zSFd}Cj|J=>LacSe=&_ z|Gy|#>%!BU9jtzLSjH^)_ptx-oJ}%p@vZ9lpMY{0TK-*s+xVYGRqBd=mcYILEH6Cj z8>R|8j`p9Y^sW9(BtxWX^`8QhV1kxqeb;O0&zh?*Er5Ft@Sl|DuQI_U13CqiS)6hg z{U_<8XzXhgneg}xV~nacxgZkqmO-=VKl5lpv?&LaZoABXB)}(-|B%c7!~fwBh7^!k z?0kj&{`q=;f})5KIRGn`&$3y>n-zv zxn%ug!Jr|mf24VTy*mrj@bn+K-vQ93{5h4|=wIhS75!a)?NpDAuj2EMiuJE-&%f~C z=6L?&XYfUrU!tpmPx2dSX^O?K{68>$ELt!4>;4*l12T=5n1Fv%FPAsw;eUtk(aHj8 z@K2LFpbH4Xfwukr7u@Y!Ymz80LVEuRu>rO14+JszzSaHvw0pB>IiVY?5BKN%rvE0V ze))*)KZC#mbmI^gZhQ9cf0jfZ&?u6ze@SwS?p}W__iudLSmxAR(fa4`RL!hEcv!ah zU5>|p+OF!~B~X*`1v9xHP$N8iEU06U?7WR=?tWXGRRLUsI?lG;L;VsM90I>2a* zzXCEMZA^hf@dudNyq1R8j=B&3qeC6r`=R80?C~u8twczK-D*{X|8}XuUSOvF2>b;E zk3-S>&Ihd$^7jwoW}`*p)|>Bdx_o~Uz1^JaOiQ_>_p|$DU3M`Xy#IltS8_i!LlEnK za^0Uuu>M!|;uM(Ixu4Hri@2>1>oI(B|M_f7@e;JO4&c+H8MR6O=%S z{fP|NRQnwWz-XKLm(IVt_qlfTde)&Fjn;0Ep?*XAIddBJqWHTS2%-H20LHL}_srxc z2hytaBuOy)!z{8VkNq56Qp@Sd53YuP{eM&tO|Nahvc7%>B9@UdnP8{u2XNjA99Y-a z`jKG;cc1u_HLc#JVg2N|O;34OXsWCNtDhh&h=)0UCTBdTepnnxVbl!RwM-RMv4GcJ zE3?hFFYTkuw5^*mV6o|+dlBMx3S;8OS)Ay%jcX-B68(_oPP$6y&)o(SH?BQW{6c_!7X#Y!G`o)hL%ip=G&oOa zK_#C%Ke+6cVR^+CB`)VL8Ug>mvqZ+tFBjd8WK-@I{1CS?w(m(_AJ40KXs4gUpj|>Qk%2isQHci z72bh*+0*g(74GcH<%6?MtWmpV=WhWqJ4pJ6SXmJk9^~adw;ttS$YzWzr?9 zOY;Nyn-T6|Vk!CuG}Yynf?>>Q{_PP!_W~h*r}b>7`C)1&M5S~_%ECD^iU6H@E%QwC z7k5SpSwgR-X!mR~&0kJ`uQC*6*I0L_`L|dwX+fAA*bv;vG{5if(;h%HB9>tJ84=Eg9n_iB zsQlraUlEIzn`QQ&{6SrmKwWpz0cYokxGnh^Y5dq~Ymr0IHS+5-l?$q`q~w<-)((rf zpxxgw%eG)Wi>(lU7NnSk^Oo8W@*84Twq${xgH6?f$4T!JT-P$-C91pyr|SW@iW8Ms_>pgND)7l?9YA?8HmFz55Gu%=C_`HnZ3jBUG6|;Ty<0* z(+>YwS@h6C1+@`p+=pBB!(+MX?g@HW$)+~n!_$YQtv$!zK zthf%8EFOM}?YS`iSAc?t|4w@z-^jyX5=e}v15}owzWSt!Wj_3N=a)u6IgNeuX@7Wp z_@xbHA`GK{oBZ&@cCyWD3L`uUrmd&Ic?q+#t-Vf?Q)uoTz9Oyhzs{9)~LT*pIyHxNZ(;KujX?ZSnBF2-?Y3;%qO zn8+4>#DFjn03hWSeg&dyh^{Ci+Cd|$=itIWrQl8-HP&O&265s4xs(&BA!W^X;h)vq zMbXK1qXUqTOWJT3ez&6S&kNYnv6jODxvTh?+lP#9K-FcTi^N9Awn@0$g})QO147?K zDKzANg+HI)Qa@cH1&$Yf4+b%PE2RKKK)k;Y?3Mv--J4om`TQ5dzszf2*zl+bZwDkZ zaylG-N*6;v(VxXJQw;wJ|6+MG8%I#6_t_ly^=sKLN`=>jRZ9tggG5n@A2IWs7RB*3W4F4942_)gre~&W!cRs))4rTabY$eqXAUmy~ z>_*8)j+fzoUkmx%hnickKZTq&!~bglmQ_heG7@XVaE9Ng@_y+z7;RrvEB6fl6H9@F z=?^S^P#!eh?Ddm8-cjA-1oP>k6<8U55TK+_=51Z-qhf}Cz<2Efs~Ka@@aJ*dE?9#+ z<79y!f90Lk%XtoL7}?&$=?uR)dk_Xch*8M!7olTKA5mCCm(`uZAJ8IbY)&3wC6L`0 zYz>8}A6VKghqm|9i>f^J8P0+5OMtBkbV45F1;5Y4$-Y#va^NqL7xA)lGI*^D_`i{c z(Xq$aC9LM(hq+DCU_bd5ApL%}Ht#w3O<)jUf7kcL7?8rl4dk>@QheW)BFStf(e$X8 zy?=d;CkSKjU#anpr;XtEd0)SCu#26|c>lf%%50%DB!upID3Jffb`Cr7MjQ#;t?S;I!D3>dk%2L$mN^I`STo8TajEh!N`-wOjgusoi(ovkEw6F4`mPz~UWGyLdhv!;M%)iJt9IV^-X5Z0L z0uHR3CM!iQ`)rO7erwT_4UJ3oRU^9ne92|?e_49-vgHT|dSc^hy31RTQ5LC3NfK^#vd(cfZQN!MY^{?o5+EHZMe;Zd{ z)3UUgRm77peATDa8_5xm*Chg)>XVESoBOtIK^)epkHGAr#iDbWV?e1dn__xY$S4Km zSkw=G=t+hbg3ahlQJ;DjLbe=P*9eM0ed+2^;mb^TSJmO^o2QZl?=R;K7jj;l5?#FM zzv~))6N+@Cyk`0tK*otsV97Suf0QX{N9wo64RP10^tt$9H>Dra`%1rGy=tPYl727( zCxB7u3rTd4K0xlZV(6U+O&_K27w22Uf^Ev#dEC`fsCH zg}&V(Wd<{44Gq=}`sa*CHvIn_BC8HkZ9WDB{ZI1G8RJY<{6NqLf038c_a3)O-NC(Foh& zafdujM?LT}pAM1pLKpusf1kw#1(x}!$01hjNi^mQoti$0Aqn$4>Lc;`_9svSUp}zx zEBsCgHIY*#5ardOPcGksknp+Rz=B%-C)Q|^x+=}?8FLd+4$CL#H*`)BNDD!3VC92C z`Rzfp|CGfe*qsOlGuV~$$$0@ zX=&AL2{e+=la{=fW5@<&n9$Y859E*}7$f8^|hGGxsSLF4!R)8^AU7fteHRkPi}1Z8Q4d`Z!H5We_zsdfPu zy4?|l2DPCUztHOq2CeuBamP*~#itQ{ia0L1D_9ZS(*r}#Yb0S4KRDkCq?1Bk6L^k$ zA|OqCj|!Fcc#K81rS7x=TZ*yIewz4_dL(IlC{J-s{LZYEf2v(LKOmd<6$}z~2b#$+ zV{ziIFnqx6fxbBLOCtL@Ar^-l&55rpWZjs#AxwNNl_UIeLa*z-qD&o5{9&OG<@1|r z#wLT#iT~j_uL)Wc+kV+5ewav_pl`PWPWkOZnQ~B!-)?&z>U^{w^MA$e=UI(x}>&RE*exKHh3p~jJVqJ^(H=P zas~fBdnI_k*@5D6!kOP!v(eIX#CnQFYAdgk7}%kB!5~?nQpkxsz z*`H^abcikXg9{ zLOtF*qL^a=j&|Z(XDAFS*~TzhTbH3Ph!)}UD^L96G7nG{2SqBL_-2v`?f^m#g}f6V zq7>!!4vYQF7kOwA5!gQ`{#qJZYr-+hB3Y}jjT8TR$0H}DAg~~?AzKk9-%l+e;KXN3 zILP{`e-MUWzPO1`(HM0jca^9P+VS`~=f%x;=EUCWUCYD0jCDf&G;u9_& zr@HLnk}lsl@oOyJ>2UyBSK`FKUdSUry-0yGOPO)WoA`{gO_k8&_c6nM`Au}@s^gar z6^X7dkkNm;&P4o#2$EkIz6c*+0ph1cw}tY2e?a#oh`Ynjh8N#4OS7Hb@U!cM&`n4b z`KD#~jAL%2wNN=ta@+@T;U`21{h1%XAt-waUvO|xq*)SCZzX)L*j7dm{#IvPe&my` z)k8S=K|_;~JWAhQ@F_>ZuZ+n83ZRe){u|-*<9vd9b>QcXGe{W2rI~6{;HL?bzeb8O zf48JgNlAz|{RA7puhqI>o$mk9M6Z9ZVem_jr;YU&I3^rDRnx@Za}@rxQ?)OR==YYP z8@(|hEcm`>)y&qilat|wqGYczRNsd*U2UlEBRC6XBzD^h=ehd4ceSRx+R%Q3qp9-! z3DBG_Ytky;A2?gFkbl%$<@nl`=K8N zLlzaADrri*#B$WvK6RaeQ(2*l8aJbuymsOW5RV;jH0EWTCa? z`_)=4DCfFF4s569`&_bWNx6W8laZ_Mq4Y&8;R2ZN>BNi$kAfOoLV$9O`5wXCbKC9y zP*2txl<(b$=I%M8BG4({lS3<5G>{8Uv6u0XrW0~D{S+}IsPa8|)cP_Ie?hvIRVuz? z)dni}v8IShg#}eC%gH_@zXSlw_Z%a-w#Ro}1M@w|`wB*T!lf1)4X~HcueE` z@qrUz*vByQ{g!bG^qTU^HstV6jw#>UfAFCJvbT&!X8B$Rs>J26ghq)@xsNQE?=L8-qI!W0FyDK2 zeOc<9&AQpk_hF$RxcYOWuZA7%Za>ZhCA(Klx%XkDN*b~EaKz>-B$KeFPZfAj?_U=( z5%BaLr74yBB_=Rki($|DIYhW5_MGDZPQ_{R2qXG;wHu7c+3gF=&XFqIH#JHSftk zC6!4@)8>n*dC&T5<|`mP2z&D$sO=igZR_sI`YP)6Sf$nEOdC5|ND3w`_^of=50N2^ z&>cO&!SoNEvdw^hf6jZQ6n1oW$ivjx6dHQ>Y#ar&IrsvAq!JK}>b7y-D`VkLy7a-%tu&$cx~qN> zI4nE59-E5ZZzN5>=eAs~Hrxdly|*J-8hVek&+NU>5cGa00A0wCecmG|H$7yh?Qaq| zOLQ%xwJwuEfB7h!#U)i;;Bm{odqzM`iP7i1W31dWA=%JvTmG6aGBt_7cWr4n$xG#q zh~8&h%1Zv?Gh*GJ_Z05NP)J3VbG^zuXi?{|yQ@y##n-M1knHlcKpQm;nur8r9hopjV3EhtW< z&XvNhQ9yji=aiqUMCoQ(5Bja>X=+E)CQRLe_0tth^`|ZuBGg_VZ)Z8^e|V4G?~=7s8cEU&A9`L?k!-*nxL;Mf z?4FhNSdrbQp$Y>0W%u5lc*f)dI?E658yyc;IlFR73zbr>***DSMTnUi4JH-xAG);T zZnJw^u1ajMZG9tVnS1pBNC^(PN%LH-qgRJ|66B>$q)fX89Hc2Q=1$=;({<=R)>gis ze{;V_cwABhaMKMENCWU67W=w-e$&DNRXO)lzx_EqX+WdSeeJ(cfmY{!MRanR;G;`* zLGcwei{4qTT?d5Ded39-vq#j;c~Pr5_Y12jq5&OQ@X&i%`__0Dt+kbc=l zKv~2(s|3MB5u?p`&i(!mXLDyPWu)g^f98B_auMf#U(H**)5H4Xqk&SdCQ^J8<`XO+ zs|yr2_eH+&!bePbA|vauhxtKCI0-R=sVRe1;BXHV@-_=LU3A<-H`~+CJevk8?kmRE z&WAm8)O46&xDUa6&szfiW(V%ITf4&x0u5(_MBkqAKzE3^5pQpnK4T}YZf`ghe@r}2 z>;oMsQ^W1G@-r|Usw5@0J^!j)xMpP<_6fm0*AxBK6E8cd{X?uZb!@M)vMq!W%g;hI z`Pzg1Bcis}Uep4QiW=05*1pd{3TN#T#sYqOGEm*0s(pMP03VwBEdvO(zuOivC!}xb z$FwJJHlb%zBtP2QK4vPjU+1I>4RgT=8vzSW9iR(o63b-q+8AD%ZbYsWfTy=epBJydF`~Bm(fAZ5O;f1lU z@vYa3Fv*W$ze>40*k3b~$lyFsK->xq?9%|DT=Tm z{%DfF818(1S809KllT(2wUBYvzwX74w{Ab$WIdY-E1F$3+hTpjH*PDio_xZhR4=hW zR@x!2J_4gk2w&9)n<{7Z{Fz{t6|eetECr5CrDJ?b%v8^ndGRw? zJH>?Ku=pAMnyf;5G6X{PaFE)?{pY+>&j>cDUH45TIVNfJCsY3%Pzsp0Rw?xcmZ!P% zPX;X^NWEP@>I<~BG3gfdnJ4fy)K4i@f_m=o`mBJJ>pp#lNEtN{slm5rNqzh0EnCD; z;Bt6#v{|(tT`j#=e-X?#{WGPf*7TO>m1Q_3`z={Nc}eCK8)7r zRrotAGnn?8uLvt0{hJmJ3$g&fveENlPF;diSdP*2@?{kT3J zaFO4Vhyu?^=#Be4#ka}+j9W<@-W&9SJBgpjkeWB%qMMW!(h}(TfJD{xgFXO#Zw~~w zjmk+nmLlvGe=jxtNy?#$2go00VdsNHr{43crUQ;(>v%vK^$X zJjq3d&KqSc{f-gV@h_Eu^z4>NlG^MX<`&mRJbBuMl*_~Do*WMN$rpUU5f zc?paX^7mk)%lu6;ZFJ~>PM3Ja3z!GE$HYfJpi6wUoW+;N^-(r*KHuc|qWE`tOIfca z*8HZgK%L8Llbg8&i~#Wf-tt_c=u+`*Lc-2ef3-ZX#@UQT--6>qKXVGP1p?(x5mCf> zLQo6KV(7}(A1vOj12_;`v2`v$x=vo}fdt0K|fK2aRoZCk}Wm^zc zBM&VddlFtiEHtpO$TOf6O2i@0MPb-=jL7ScnN=F{yqO5J4xVuGxI}3~8Q6}Pld1@7 z8bQ7_g*FV`dAtD=!+rdlAVZ}`_jpplf7=sU-tI_4e@HMRSzw6Ap?YgPMpo!Ryg=64zOOsqhXtirSYE3n~O0#SGt`e$_os{rc>IdI2u}R^GV(|9SL2D@c zIj)vbGqT@+SAXy{;P6GZWR!O|Qh@LeK6XZ-w8*W2@Onu)hTNPketPboFcxk$pR*5y zcYJl{=Hc=v9)#Ofw1Mz$e;&CAeL)@9dd$ZvZ7|hkpmZ zlvY`jnQrjRdFtUD4^Cz9%vD~imP@UYZx!4u;&VK6R7u;g~-xpMdsrb+rlVmB>zoxxl1s+j|! z6PcsL2=HS=9wAl^r<-)ca1b)?q;OPCV%@D;@awmTR)U%ZZvwc%zS1wn+9kX{e&FMw z2(#dML8P_d_Z<6=f7rAIKLhlO!n8U)_0!5Vbtn;Do7)5#h%(kmPgR&=%eUaKOH+aF zfFq%V3m&(xZ1A;xUGT{a5GoH%(BcK(sS^Ulb)rK-I2SxtFJ++99oC+gg$v$Tb~Cnb zMXLi;$ps%8viPniT&^=KNDfgp$SwH&b@?i`w%}W@ayjzVrg~e! z1hL>HH2zr)8VkPBT?V+Y;8*s7u(Itb3to|I8fC;b7zu^@am9m1CoTAjhNp7UmuA6Z z8+k-z3|1eQQ1CL+%_I2L8`c7Cl0EQ&C*fA#A~F7qfBaM5$m<^$aqECxWjNi{zs2CL z8|SIwr+5*+y^w(i|6Sj;^`f9~Q|bW|VD0S3HK!ReckIZyk#lc`SyUvFZ;XR4 z8`v;vjAwEuF+y}NeZX3bG!rgyx9?FQAihw-835G~G^s#gG8BrMyItj;sicD7TNse$MG2><83SE`b=>g9*!{@4D z^xLAn+2D|pa4H9+k8rxc6e67cb)__m1B}z_e^zESCKq=Di#LyEQIQ)!Ae;wlXgq+- zu|z#Jol}8u)+}Y6obNVRAp{4c6wk}Q_ZkNeBWiTf|t0|nd0a5r6 zC`y?xEC8f2%7R!%>u~~8b6gU}EfY>zF&+o@U(y|Cn{Xf#^Xr~)kih&3e~zdqbtBdE ze^{neCHB&vCs&1A;$9vcK_J(EsvexI19iV8vW)mxRzot#BW2`D<@N1gaSk}$ulN2!_El&>L_@f)6wV#R>PSnLoiwr}JKbjb%d*n16i%!b`IgXe zV}vOjw7xW=$I8^DGcbiydKXy(yd7q=e}qcbw*ypC%`l@EzFp1e;S%zj>Y=}(!;Bb{ z)r=&lSU@=Ub_znL1pb$?AkdSxUEAx*Ebk2);81fUg!9rolifeRJOBp<;jCb7QRNi7 zOa))oe167({F%a-^;OH8ZG#*aYm=paA<`|)|=nrd;pq_bb&@Gi+^oK zo25OqVW0~S7S;t9lZCh9g4gR_;Tf#o1QUt~KZ3fQ3~fdQ9lN@Ktl>ZilzR=;y9r^O zM%f~Hy&i8@i>85GHzSYRv3)q4kFIn>R6ut$^%6f-@c^7}G~+ zb|k^sIK=PT@hGq6tdh~>e+0cZ!wFb|6aQ8|X4ED_(!nI77pRvqvLYZj6lhRJv)8Q) zp><~XkGI`!aKfY-i5$uS)DeU-k~4LxcG8hif->?{>;XCzl)MIKFttQP6AEZ>40r;r zHb@Q5x?1B3B814v2tFXN7s07CGdLq77`1IPJ_*`r>~^1v`LY~^f9Vrsa7wO5v44Tc z;qPR`h|v9aY%y$eWh8FUpkaCUX@ygq;p5D-;Z_Pu zB@1V;OP}GT*Bf6(e-Ow%IUE4}Qfg%c&uyhE!j_zw9wQv6E5xf3-RT_RJjs~k$Z>@b z^F}z9TJMXDy>VxbaBy)eT<`S_bl(Wa4P+p_g(LV)LKtZq^ow{4r;2V%HXQoTq_u@4 zN;Bf4-q3bz`;nrg63oc*z!D`tQc8gwzdjXeHe@}fdh|tS0qsc2wEo#)E zhZ#vMM>TWO0J>$t3p69s{bo54kZ@ciXo3Y!;Rx2Xgf-Q|;fWT}o>99UTR3X>4u8Yx zii<^63WxJ;HAh)9E{@Hph-3;3KopPKlqe}BErzprS4D#i3XkD*q$IpCTaRi~w!kq8 z5@6+UDuB;de_0x6$3(0uEVmGc#(ae(j=6~`oo>-`E=+xe?AVr)qPsF!uEk*)1Q_EG zVy=XZ;{dDQU&mp1l$xwj!#VcSc-=J;d7LxYrmD({FB+P;Dkc4nmhdNLYu-uu$&Ml$Nf+Liu1K07`0X(>g z5saRaae36$b1uG?~tWI)-P# zqm=Bl6iBLPAwI4VlXeVlAK~W;z5}E{*c}9t{RbK;+mBGQ(eQoTWS}Q#xBuJg_C2bNUY9w*he}AXU3v810(3VN=dS3t5v-3zyoGu&q@>Cbf+vkzM+$9?PNS+^b z#xKK!^L*2k5c*G2miOrgqdQXJl$j<~>^Llmv3P|0oHYN2hSG#r z?E=!71VrY2<`-|uByNH3#*YEjViNjV<1xij{$8JqnlUTYfPN*`#iV3jZhdAHe~Vxe zIc{!B`!R$@ATJ4AHex9R+JIC{A6oI_N;wvgF@1{OG_8~5s(DGeUv3ycVW`keLm!^h zE-z_9@uc53lD9`9M{{g_rVn-Uml0#6N~RCYTb1HhM)wCDFNp_Id^R2`Jo7w~vIXGE167 znsf~$_S{ZUlqEe2m8PnR?0WD-Rda33xF^xxgP zG~B0{4#od%ClQ=U`W^gNl>bLGK=e$4<9;{mlxEMQ#jK9YN{nZc(E?&WmPk9&> zhLUgq+f=y^H~##z5k8S;e{_@ke2$;a4QCY4ndHR)78apBNnP%<>wjb)j6|&IOd63* zuh)7cy(p=N@py}0$3a@}m_CI(do$6^HV~I@wf073Lq~={ZJ=*R8@YDTTX#@CaaoFq;;?W2Ac_3^VE(GryX21`d zJ6l?$I)d=`@ho&rYWAjEwR=t4FXRi0mMyx8tz|fk!k|OBB})srIt?h{OU|EWwM}f& zz9v;G2+J=|Lo;ab_mSEFg5M2X`u9P`4pe;C)@3F7eV70Hf6!2aJ;m62^80{9-7*zt zJbAt*E&Fhkb+*u4agC%?{60?LxP)96mHjvuA~LF3)~dPecMY`Y@W;|YxjvLE=zX_! zN5t$JTi9UaC%)XL-N&ByH3XR^sg{fHWy^a&1TqWDbQRMimeaBmvk|+`Q0z2*0v4Ml zz3QuxEl2_;f7yMa(43f3_vt{oBr`+1ZFfwQn1Im{LnZ(rrDnm?q~}>nVQMF8+!lhD z#c$Iv_A%;dn;7jr`WHpo5O~PnX9a6gGLBa`C*m4Zd5X=yPwxzm_>o=|p{n>$MJYbv zOI|pMadLyAn3*RfoJ2KziDu{oJo+8!xf)`eW-cy!wnmaBfcKvP$Lsg!u}fw zVcJ|kY!(3`MVd+C!B3^4^W|FiU=G7*Tpdg2fSNJHcfEdI1=A-?_|eA?x8CsKBy*-d zK$)5Gf6B-|+oQ%$L8^F9fwb)Sv7&~3p3U)-f5t85vnmv~IgTF^@IA(j@>_&DCviKh z${#LnyX9qAy%{A6m&ya;%hM&aS$HVa(Gae_gnlP?r zsVr-Fh93uyw^jxV7iUb(U?0>bZ1K`C{5bit{NW1tA3uITKt4S4ljvtFC;B-~*)g$x zf7reHTJ~d;vxRLV$h#kLE}mhaR4U=;nx!VwLLL6B;>zKQuJ5cIG|# zQAV^`L+=MRZOW5UUf9)qy&rn+(F0oLSO=gq=WBVSv2Xp}kH}dWe}wAv;SYk+&#({d zj9i^ku}uES*?9s4CB({jZb%{sN}*s^e=r7uk{H?!mp}xT5uf7^GupYQ-bs?d1*p*; zvb+9L8-P;rV&EycPfC01ti_+Set!2PRMHXCpy`3SCwUOQ^bOk&jdrpp!9kObo<(qv zJ;`N&ACQC>QzS+(Kh5+jdlL4uU8rgAXqkSZpH7*yNppnyBxznK!Kcx0IG5^^e;EFt zCId_4e_=fI!w^HjBCtRQm1Iv!a6;IGD$O*0G84u8ghzA8yCedbpXavoA@+-CJPAlv zNmdZctDV%8e6QD|g`3eM4>^ zKQSGa-)H(mBAnMwf*GlRm>)-2)U*Cw1nbTM^Mlg{z($65pu>E|lj#2w8)C5D8yB1g zB3Z}xJ$~#BdyhPnu_{!`X(zc9nclmPAJVnYdH@kjxajed=0^#@ZMMe`e+Rs+9m8tv z$Ioh}SjCchagQI_t3j7@+~y6yw5^z9^;EKQ-kG?(MYlPG+K(`U{&e)#wGFt*fgCncP7zosUH&i~5sGi2`1 z$pU}@nmc|F$lF(i&X_&N4=^qh&fiX|A*q&e0~?jD!VlX&b;28d%AV;fLP&m+I_bSr$L|uv z&mghO$Y-I;&#$m5gyO1nB~0UPgZ%(1IZI%4_Fv#1wBho3W1*ZO@tsf_p`oo=D4 zlirm&?=u~i$@A;s2NR5XHmBu19DeMUuHFAMd z1yvk=(mqlhXDP!(_2DOC9SnzB@eOH^PEy&Faz^tea=w*gIQ+D*rR;kLR4md-NofL! zJP^Ee5;N*%f9dE5$(HZ%1KfW}#?~?%om6zBg=7<(MAx;Q+X0gLbP1BCO9#2eOD*=~II$<8kS*A%z?tT;a`9fuXxdFX00Y8#qn#?TD-BV&ghA`=hogwC7(|;dTCPMfg zibylze`JcrqZ0DGzt2H52d*sBQKoe5H^8Vw6Sx+<>v|)KlF;G$Ha=QYEJGX&KWAU#0Rn~eo(Ck-ah0` z($k|i;maR$CzaJaW`f4f(4Y9>&mO0c2_}B%e_e-bAr=G)@k7QCnoSR-Pu__iGb&&= zc0FA=@k2;lLyvT~gbr%J8$U-APQ*a&;#^{cQ9(=ZASAkyQGjJV$FO)pq@44Cj}z z+Yc^-KZu^YnVIq@DNTP|ZGSw)sON>N);S4`bN_r!#XT%3l_Zx30f5ByD@b)IiGx0C z5}?DBY#bn$5|EoFrTqf3(-K`79MGnEf9-^9H(zL2>{P;qE`{NkN|^f{F3=hcQ?3jI zq5_EM2Er?LSc<{NlTA7Tg78JU3NgsMWWkXuNdyk@+5CMK+a9|s@qQEp=i#aTmF7qU z^wRyXY&3p_j)C{$`1q&d~IA`g#bM+PQjB@_s@BH&l+CovGkZK9M1<9BOl!YNkVUA%NN(ridD# z!8`Xz5`@5JD8ZD&L>OfVVl5>G1NE+MF*lw9bBC0ul34&^C0H$nHoUxef2!bcL-Pw0 zC}cmYvl@}s3sVA2Z_GVekj_LoboRWo$X%^#;xK5s8H41d1ni07)CL?1xyd1nUA7{O>D*wl9clv%b~iLZOaWA(t6*zv4^_?) zRoaI@^K0mff9ReAk*eHAf5RJ7jH@ESr%xDeQ6ob{z9qi4Bo9zxG7O@}jHvtO5w)yH zgu3fYP?|&m2Vy#MgM)wg(7f; z;d54G{oE&jgmfgK8q`FjiuBm@uuC)*DJ8raty^|Q4F@WSMf7vMe_?G))8M-kFpKig z1H-Hq6tgMlGaxhcS%YUdrrLF(UM&1%6HF zo~L3R?nUAyjegX0bV{mGiOsQLUouHYV@D1nMVap!S(9zzuI~;4suQnlQ>!5=^Bff@ z_|zs2zC7A0NE%6HzjCWPV@PdcjwM#HKj7%8Jsz~l4W2Cqf6Hz}K%=vov)Y8A76BSD za7G|af#?dKK%=f@*FGLXjsxpJBf=jHAUxfJEcAgkWh#;)ySZDa0FAV`IrJi5hX-w1 z9W1&i&aer$xdj?U$d%M7Ab$KAHC_v3i!`DX)#WF)i5Cw0>GO_lS_srjtPuw#syM?< zDxX7D<|ZUPf6?OQrW?dDfw@sv>;{x~(>O?b_*{D4#FERj=zAl4)1*984^8-cDO(IW zx)|gTEjU>@AYNiekQ5T5?g-B*0h|nvn)-~WixbDs{MG7F6&?@l1?1$-B&TH7+rN)~ zDW{8IJ`(w(s}QB-9e4W4;7#U)ACsSu1xS}|j`fxZe~@y)(({sRsL05isMA7ibSjYf zjyW-Xtgps@!}=p;vwop_^Y4$Q)=hwpb~|)`l(rA8_kh`_ksNG+8}FZzz3u%`^J?aG z%92>Q@{g!q9E13r5w@JNo158n7by(;qYk=M_5t@tI|qXJBNZhO>2+8S_g3PVCHLf@ z54rS5e=|VeZz(+g)B)1mF_inZKo(ul_H#+vi6(Q>!;k2}s3j%0Rexl9P=sVo6+wUW zQgYVVu0IlAfMQ02JsLxQV$f|ladVIW@PNcQ{SmKnU_C%$rk%yn;S419+=HzIu(FJS z6q5(>9gV1bnv?Q+rp8@J9}vB1zBq$fHUl}Ae*+}Zve+3&Xk&Fsv#`IU0n*L}A=RAT zg1y|prZguBNU?1AQI(_(V%l#}&FP&2CZx9Q3#I5!bILulKQ%!x+yf+xHNdwRljfwa z7%vuQpA`;}J~mA$RVtT&#ONW^kWIV&-b6ssbr9s7EZQuQ$vH(f&6Yo7_PQB<_F{n~ zesg@PE3Glm?n=z@^oU84DMfgpx4UBo=!GM z7ubg%z3u75yptPJ+T>~p$>^t>IDfu-rIWdc5W^G-B_uFE16%-x=7nKYIz<&@f9O-U z4b&16BVGC)rH&O^wdP+-NJU6sG-AY~3Q#X01xdLPHQV^-izy+ch@YIKV$*Wc$rQa+ z7Q52&*&t!j?qr5n|5r59AQ^HnLXtDwn@&S2H~B8*d0_%PS>45Jgk;A_x^|`WzAP!7 zCc#$mnJXWP#l{Fp8c6Qo!!;ol&6wine6r4ThXNQG)n>1_nSU`K&ngMMTf*;1g0h9UY={e{f2)k~K(=9VK;NpmnmRLHg2JnRtir)lbPL{_=MX(&9U&DZ#~8 zLtjLOv?8`n)H1!pgVXM_f1}9Z-iVHnm<~d*kVwsCYDnf6U0iUTY|m4P{x#hIKquFi zB`8v}sp(D@spw(<2e)K*DVuop;*K<4hagBI{?5cAX=U(l!_zHCv64)?d863uUXqhRGc6`vw%Vg}CZ#+mZO}CdYC_R`aC4=rGdp;p&k@HVCmwc4GOrPe{e}FNi9(DGd z)+v$eE#@97O;f+7SW^Y4muhkDC#yTQO8?BsvIB3< zx1U%lfB~Ma0xW4&AnB{WlZ#T9QSB1IMSf5j3(Kv7vnIo>#Ou~ZcS zTZpCjL^4wnpcF%2_J-`Vl8U9xk`>|n$Q26}OO7=TWW*|#ZW)Zzv+^SW>I;4fi2_8s z(e1ussb!!Bhuy=E3{0`q0Y>Ht^I!UjrCmA4rvmUf0m>>emSV|?*Mzb-9~OX-V}r_j zZyBIe{$+3%e-7L~fP*DVC<@IIrJ$pYL%|Z;3(4X4mA4eARp}s)hqNUEgbYuO%>7@b zY1zO^H4QYdRH>vRU}-4XI{r#~+4s1D!tq|xzT3h|Sl{MRRZf~0G@GpkQvh&UHpZ`% z<}6glH4gkrfv0PiJqw3`_?7SkPvUVmU$*>8YhL~me_*nVU&(lCNGkS~LRIw8aV-GK z6XugsF*cM00Oe{oM-E#BMip3B4@<05`k_i=)4qVG_JA+30HC~;m@{~pTF|2cb9qf&N!9kMVme zkuZ^2e{EIb^|O?Nd<=vE83o05OQnAR+ZQs=PfiM-`_T8Kb48Q+-YHOXq{y$aT7)OGH+VI z+tlrRpQ$t}TG=yLS#3-u#;1n(kq~boA{Pcue{@cVqZV!XBz#hpOcv-{gA_QbQqkZ8 z2NwhY*-yn0oe(N8-ps3mN1*Y!E)%6n62L@QzXmR!R4Ml>5hVVkZLPFqC6;g2w$h|$ zs&m65xMSwd|4QuDhgDB3H3w`l3#c{^q0VM0J^T!pw1f=DL~v_qMdL*WiqEjVEu=G`GFun3kzkAsK%t0e+-56W6r-tOW$Bss)bXih)iMSO#dUra}he1 z$mGIKRp&(}5K+garB_n$P2eg5@l|BnqV?nq+a1O_AWp-jsmtVGBwX(mY8g&VIEYO4 z-B3p8aJPzxOsYK^;)ny!eJJ(CUJLVC?z3_$29EjN{vgbfjJl|!R(?2*h&h7Py#BH!&Si6^LjV|Sd^PZW=yx^ zyToXKIDpRiE_J+0JXx4X1Cvpte+womFiv?)jnvDs@OUL@voY{jeZgaLvs{=7usR+S zxKrm1y9yHI9ozV4;xo}vLS;x{D#%iRS^iQop=sYBLTD6IYvDx8V;UoC)+;jgs)58P zrDerT?Fge%0i`sQU%-x$M*$4A$Rx;BiVW5`0VNojh?1??J#|@HJKLYMfA0@qPpL8O zI{$%{2Cz~ccw@50q|l#Mlquw?0gUowSr28pTT4{oAVZnZXti1T%hzl}3CpTHxz^aS zzhWp8Fsq5>bVUG`WlPA$QYQF5{X5yHEJ+$b+9;i@?9taW6Tc%o=B05=y(Esfp~%xu z_nN&q>YzLU79Xne``Aykf!C=rx2+qlWMsjCE|OSxuI6g1ccWQqF8tUQ>()% zF3)D$?on+RB_}t?t{aLqbyfMm?wg)el&K6ej?@KUvWQM$04Xl6TQ+7j4ioa1e9v~? zI9DogS*f^y8j7CGRTUL$9_=}S%Vh31XFREG`G1vw*t$xOwq;LeCrjfMr12^Wus%G}l1qTeAJs+*m| z$E+7NfEhs6BYh7x?W{J0Ex zE1Oop_6+#4#CV=gf0EGq2d|fg`ieoDwOEH_TCyi8)2g_MP*@L2rA+_Y3dzd9Ipif# zXMxj*LO&(Mq)e%VpX=UKsoYR?g;J8gWC9ESbhoDY^hCsc@nA9qXdlu9NDTl{K(4

    7d zdbUEQe~CYe46945xA-v>-es#O=6zfGLZXzRywq!CL z)cI?cH=KuFqh~F!`SX4`l4z)TO$dwK#_~Ym>NiKc{xo!Q?(n~M6RD%L9BD5AWO_s$ zfx|#1#>TzL-Ao`rCV$V;(&u7^OjGGa^r!m=07E7)3g5fdnHHKxgAwy$`;a&Ly3jmi z3V>G}JxU8LOsMC?xuHJU!oD2p+jzq`6gOL!2~%b|`W1IGIXK?zJJafOgf3hBhS|(O z+THk)CicyU-82cwc_cNhWbRu{hn)fmwkEK#hx#?Oc0tUdqJQ{NQbysF`rnYq-ZqU( zM(~oGWR>5rvs{#fb<$@qDv`rkG?Oq&ALSePO+lGU1gABfTQW{!K8raWXB3b{q)ekA z03ng^CgSs0a1+x95a%@8G5EUl0JbnFo%&LB(i&5zXt++D4$q*9vXj}}PR|Tp^E79o z(Cu+8U$=VdlhH?NrE>iy;^A$LswXeHL^R|++;T=L0stgA1a(F##f;|{aXLT@J^9d) z+}im3t zvdXl=gIP~4M5l+_X_Zc{$gC&Xz;B0QIyh2cp{VcVmEt4-gOMVWrZFeR^z>`(jTDHT zm&6#vy^-Py0wLr9B(*nNC^0GJ*xOTS%^LA6Kj_<&d0stn+

    pPhRhZ_)n{6yeT3NRq4VOfE`D3eVFNa_&T7WmX1 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

    fIDY}Xcl1!T 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%0AA4}+ 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%0D>-cnOf9l&GFZX=bQVhLv_(Ug$NEEb7e3( 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 Date: Sun, 19 Feb 2023 22:40:31 +0100 Subject: [PATCH 025/164] Add Fn as a modifier key --- macos_keycode.h | 1 + macos_keycode.m | 2 ++ 2 files changed, 3 insertions(+) diff --git a/macos_keycode.h b/macos_keycode.h index 839bc7605..05b85f990 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -9,6 +9,7 @@ #define OSX_CTRL_MASK 1 << 18 #define OSX_ALT_MASK 1 << 19 #define OSX_COMMAND_MASK 1 << 20 +#define OSX_FN_MASK 1 << 23 // key codes // diff --git a/macos_keycode.m b/macos_keycode.m index fc7f428a9..a4437a324 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -17,6 +17,8 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) ret |= kAltMask; if (modifiers & OSX_COMMAND_MASK) ret |= kSuperMask; + if (modifiers & OSX_FN_MASK) + ret |= kHyperMask; return ret; } From 2e2c43ad38c3b2113f4b85bac01104a03499e377 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 19 Feb 2023 22:49:04 +0100 Subject: [PATCH 026/164] Add Fn as a modifier key --- SquirrelInputController.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index fbbf2286a..45f0a4ba7 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -121,6 +121,13 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask; [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } + if (changes & OSX_FN_MASK) { + if (!keyCodeAvailable) { + rime_keycode = XK_Hyper_L; + } + release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask; + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + } if (changes & OSX_COMMAND_MASK) { if (!keyCodeAvailable) { rime_keycode = XK_Super_L; From 2ccf5a9b3232de49f710387c96667dedb59d0859 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 21 Feb 2023 05:23:45 +0100 Subject: [PATCH 027/164] Ignore other function key mask --- SquirrelInputController.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 45f0a4ba7..94d4c88bd 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -157,7 +157,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender keyCode, keyChars.UTF8String[0], modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + // revert non-modifier function keys' FunctionKeyMask (ForwardDelete, Navigation Keys, F1..F19) + int rime_modifiers = osx_modifiers_to_rime_modifiers((keyCode == 0x40 || keyCode == 0x47 || keyCode == 0x4f || keyCode == 0x50 || keyCode >= 0x60 ) ? modifiers ^= OSX_FN_MASK : modifiers); handled = [self processKey:rime_keycode modifiers:rime_modifiers]; [self rimeUpdate]; } From 0c4803269f8418c098e75a256e324fb19bee3dbf Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 19 Feb 2023 22:40:50 +0100 Subject: [PATCH 028/164] Update librime and plum --- INSTALL.md | 2 +- input_source.m | 6 +++--- librime | 2 +- plum | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index ac6999e20..f116f726b 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram # ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram # rime/librime-charcode rime/librime-legacy hchunhui/librime-lua lotem/librime-proto ... ``` ### Shortcut: get the latest librime release diff --git a/input_source.m b/input_source.m index 4d1ad8742..deebd080c 100644 --- a/input_source.m +++ b/input_source.m @@ -32,11 +32,11 @@ void ActivateInputSource(int enabled_modes) { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kHansInputModeID] && + if ([sourceID isEqualToString:kHansInputModeID] & ((enabled_modes & HANS_INPUT_MODE) != 0) || - [sourceID isEqualToString:kHantInputModeID] && + [sourceID isEqualToString:kHantInputModeID] & ((enabled_modes & HANT_INPUT_MODE) != 0) || - [sourceID isEqualToString:kCantInputModeID] && + [sourceID isEqualToString:kCantInputModeID] & ((enabled_modes & CANT_INPUT_MODE) != 0)) { TISEnableInputSource(inputSource); NSLog(@"Enabled input source: %@", sourceID); diff --git a/librime b/librime index a97b804cb..7e7f341a8 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit a97b804cbdcd7c5342279be80e269be66a01aff6 +Subproject commit 7e7f341a817c5a077299b0adeced3bab01f86377 diff --git a/plum b/plum index 3d06432dd..6f502ff6f 160000 --- a/plum +++ b/plum @@ -1 +1 @@ -Subproject commit 3d06432dda5fd1738bebda298c574b6ed2d512d8 +Subproject commit 6f502ff6fa87789847fa18200415318e705bffa4 From 6e3d04c7ae34b0867603f786bf4d01eb84df950f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 2 Mar 2023 11:58:36 +0100 Subject: [PATCH 029/164] update icons --- .../RimeIcon.appiconset/Contents.json | 20 +++++++++--------- .../RimeIcon.appiconset/rime-1024.png | Bin 35197 -> 0 bytes .../RimeIcon.appiconset/rime-128.png | Bin 3943 -> 0 bytes .../RimeIcon.appiconset/rime-128@1x.png | Bin 0 -> 6198 bytes .../RimeIcon.appiconset/rime-128@2x.png | Bin 0 -> 14335 bytes .../RimeIcon.appiconset/rime-16.png | Bin 510 -> 0 bytes .../RimeIcon.appiconset/rime-16@1x.png | Bin 0 -> 1489 bytes .../RimeIcon.appiconset/rime-16@2x.png | Bin 0 -> 2234 bytes .../RimeIcon.appiconset/rime-256.png | Bin 7751 -> 0 bytes .../RimeIcon.appiconset/rime-256@1x.png | Bin 0 -> 13917 bytes .../RimeIcon.appiconset/rime-256@2x.png | Bin 0 -> 38775 bytes .../RimeIcon.appiconset/rime-32.png | Bin 860 -> 0 bytes .../RimeIcon.appiconset/rime-32@1x.png | Bin 0 -> 2289 bytes .../RimeIcon.appiconset/rime-32@2x.png | Bin 0 -> 3227 bytes .../RimeIcon.appiconset/rime-512.png | Bin 17162 -> 0 bytes .../RimeIcon.appiconset/rime-512@1x.png | Bin 0 -> 38775 bytes .../RimeIcon.appiconset/rime-512@2x.png | Bin 0 -> 117507 bytes .../RimeIcon.appiconset/rime-64.png | Bin 2018 -> 0 bytes rime.pdf | Bin 324081 -> 324597 bytes 19 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-1024.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-128.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-128@1x.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16@1x.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16@2x.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-256.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-256@1x.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-256@2x.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-32.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-32@1x.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-32@2x.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-512.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-512@1x.png create mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-512@2x.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-64.png diff --git a/Assets.xcassets/RimeIcon.appiconset/Contents.json b/Assets.xcassets/RimeIcon.appiconset/Contents.json index 7c8a1bdd1..6583b127d 100644 --- a/Assets.xcassets/RimeIcon.appiconset/Contents.json +++ b/Assets.xcassets/RimeIcon.appiconset/Contents.json @@ -1,61 +1,61 @@ { "images" : [ { - "filename" : "rime-16.png", + "filename" : "rime-16@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { - "filename" : "rime-32.png", + "filename" : "rime-16@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { - "filename" : "rime-32.png", + "filename" : "rime-32@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { - "filename" : "rime-64.png", + "filename" : "rime-32@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { - "filename" : "rime-128.png", + "filename" : "rime-128@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { - "filename" : "rime-256.png", + "filename" : "rime-128@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { - "filename" : "rime-256.png", + "filename" : "rime-256@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { - "filename" : "rime-512.png", + "filename" : "rime-256@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { - "filename" : "rime-512.png", + "filename" : "rime-512@1x.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "rime-1024.png", + "filename" : "rime-512@2x.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-1024.png b/Assets.xcassets/RimeIcon.appiconset/rime-1024.png deleted file mode 100644 index 422fa49033d131a9ba4585dc574d2350597a0172..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35197 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 CFbD|% diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128.png b/Assets.xcassets/RimeIcon.appiconset/rime-128.png deleted file mode 100644 index 924c79a3a25a661cb31f159b20c7ca8b53988dec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3943 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 f&)fp#*$TjLQhtM!B*y-^JXgD?p_Jm~)c%uHJ- literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..924206df532e6fa7d956a4d46bc05c20441d3b0c GIT binary patch literal 14335 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 Wt{+0QTJra|Hq~dEPpcKILjD)A6r_d# literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16.png b/Assets.xcassets/RimeIcon.appiconset/rime-16.png deleted file mode 100644 index 45a7346ea5c53d927aa5e5b602be6ee1fb7ccdc8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 510 zcmVLlRK&+VHAYFmiDTRE%*q0fEpOO0Won6qPsD13zvB? zYY+rip$4WVB8s4+pr}1Q?l?+KFxa#P7Y2Vg$&-g+f7qEX&elGEt>c5%3}D!Nd!|dcEd&JOXgL-B_(wT(4IEPNx%_ z&E}#1A%*dHOgfz=kw^e=y-CywG|F%|q+YLcxm*B# zNs4$)bi6Tz!(mmcRgFd?)$4WFSWh{UFg*(10CKq;$z+nl;lOUUBOZ^l-|xw0vsjkJ zYPDiEo4Ng72^YZMY&NOYYHYV#*LlmbToc>vHjPFD;E$v?uY|9F0)c>n!JrJoco^B| z^U1O-g+ihG-;d`-x|1yU{1$!!-{BwDc=eqB0yXg;Y7`f+Z2$lO07*qoM6N<$f}zUa AE&u=k diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16@1x.png b/Assets.xcassets/RimeIcon.appiconset/rime-16@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..842cdb4caaf94f3af0e431019817fe00b49722fc GIT binary patch literal 1489 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 aOKuT$T{V;G7s}QD$P`kb?>D;{S^ovb#b!4E literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256.png b/Assets.xcassets/RimeIcon.appiconset/rime-256.png deleted file mode 100644 index 91cb364f58a2e2cc5ec3025f1e11949a3954f945..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7751 zcmch6^4V{}xacs=*4i5uvb8*`?~(RwpCT$)oQ zB{;?jCrG@AlU)ZU!(tGPY|88o1q~=)!L6PgRhGj8k7JK3IRkD22fM5`ZMinzv%Q0b zUp7K;){i|-UN8mtzA6Ntx1@j+#8YuX5Tu+imax|k|JN=p3FeWf^x7&4*Br9WxrE%3 zAHu)NA{vIdLvnMtypNkxzYr%tPvzmXIQ&p?2-BR|TnpKy(;a2r$pxVTaz`Abf+dM7 zQ5A);jk83@Q_0Z!kb86W$n&LSc34g%E;10QJjYoRjkvXXjO<17AhY|q9S67&E(noS|e}ReD*Eb4Gl+S@(+%zu-vR3!WWsTpD)8T@3SF zP_+^3bIvB{F$uCVwD3%yJ&aW{L@HGFvj7V$q2>KHLc7vJRDsqGIuUnO=uONFBA%0+ zga0{ITK#mpiNwU~;Q0tSQelpfr6d>(bAGi!KUuy+Deg&=lf02oTEx`}w~9kdm`xH1 z2fotth6TlY1X;I>s}_CzfmqjQyetPmTB>(M^@2m zRq{a^IdnA?b}yU@H{zHi-y62{u4PRNE?Db}M2x%B%(dN|%0d+W9FTqL0i}Ai>b;?` zdd4j1Mw1h_ajI#Bs7Lp2O*cKRvV`SoKQlyqE-jrOF4cR1u8Y;@HYgzM6j=+s-(*JE zoW>?l#Kpg~>$9@Ho?lmIIJEyD7ePWo@?)W~!siyhi|~$s@_~ldpvg&$6!8nS1erG; zOMN$&`qCZ*2Va$EnOIn$(uD1aB@nkRe{CY$Xa^JCbwP$V$CD9-8w<*!Hu{hVG)6lN zPPgcaQt7pcm2r`?bi;87ua}wRv9wf>-fwSe1$D=$|4xWVo~yu{j4c(}!7wVkTJ|tA z5&FS5I>j)Jv~y-+ywcKAD-Vwkifs3}li1SK({-Y`$eruyGL*bRn=RgO~5|M67t=@;54#aD`K?c?LL?^MH#d4K-+ zf#Y1CfjK{RUrAUrbf>6s9nuhcc|7f%sLBDydyRtyd)3a8l$3;n6>4gKd>YV@?cZX4 z5hQ11=1GOFb6RLv&JxZ)c+O5I$PICmJYQxT85ubVkgaHy^v}kvN5`T!vv!{rhRVxh z<@|6OuL>0iTvZbe3Xsg?%+yTmvDxu!(Kj%l`qHQ-RA7~lg>=!D(xaoJOFyJ?ij9r+ z@bsiAWwZKFV~@fb7#kBSb3!h<4Gs+C*gE*PoVmlFi0q0ah+K!u>S7qO69;iI_QZCw3hLPfOu8d(3rmJS@TSXvd)uPnJ`bq z{J~dQE`Rw$SYI$gPvT)E+tgo-)lqI?1jB7s1Wed>!#-onukUE+P*OyNO~Jq8{oNIh zQe^YC^sf8b8ufwD1V3c-z^lRZ$HGejh+bRZOqWt=X#W=U$e6Vlme^CUfi*5KLYG92 zyrl}Mw{Lp&@`q@E75uyV4F@T5{YVtnH&cEP`z`ds^6AsO9Ej^c{r$*1 z^9J7KOiy?b8PUK7{Vt8LkW7R(2^vlL(b=SRo75F}A<3HM<`5f9`;71=?Ax?6+JeFv z&DWgpkY&|_E*oWk9>t=mt(qV;|02N&8>-HSKEvh?{cb@o~On86xs;Vqcw4v51$%%03IdHL>8U*OqNJ{#i{f2?@^O)qvvPEx_7Wbp~b~TNh6N8 zhK7d9EQo;s7SxvGgK@AD{F_Ot93|!D1&xylROw3ORCJJ+1>>(#SPnn2gh{DBVcV?B zQlqQF?X?Mp&rQ&i`dnSBb!ecmv9j^Y@bK`8aqQjKXJW&C9Kkj=ie1mkyGCJmrKQVP zeb;{enVkG&E!sic-`^i{1i>S}`z2dT)RBe|(eOhx*@J~~5Xd8s%7?US82aB{H1gAS z!%p25p!(&uwwhnVbZu>o(C>0!AVD+9IPo*KrK>AqfC&Ow=aBHsdu)$$#sAh5hl$M#fxT(oxIl8=a6y|z%;Na))e}{)Bx`yq zNXn|MI^SVtKtRP>+59V~Jci{1#R#i!3g4RsoQf&OWueQ9WjX9g6b`Rhqds`?&}81n zx&!-KAH{R^ZjIWWL*hXf;%Vy2L;W(!%F4!#9(RmPUn-Gv-{>5kn(Ff$Gb>(obM@Rp zHf^_>g#9Y-Jc8}~mfCjx`-9-qr%!g6DOC;yB_&+Pu?ju~+q~4RZwsX0x`@V7X+_1- zkEk0AGJg739lSq}V2JoG2N#zy5iD&1=9cw}rUQO8%85+x8G>gfGz060ZX4r*=vfjt zcoWE_4RbGN({JaQ$gj1LGEp-%mOgSgj&X~hDBzfV=>&F_ILdsH-j|9{^XXCsl%DKM z8m=HBB2x8oLAj-uLa7=Y)6>&MH2&$-lY3@-ky61y=01oN_dhBxI>oF>b&P&CIpljp z8GBa$N9dvwl*o;Vny5|iiwVzeiU8I?x$UCdSamv~IKI6&Zn8nx^ zHb-}LXaijz|K*FIMlm*(5-S5~jqko)8oi~Jkwu_rhL{T{1F56rOn%nAi;>D8gRr&o z9zoau8r|C(cwSOcGVs+!Tv9T+ck$_1g_%-`=E}VP>^T$2$HtS}i9OmnI_;O3^VQ8w zJ_XOwf2k03(kT!Gd)CBjtV4;1kN>zWNQVC2i-9H)ogDwIhDfb|zc)V01)C2wwKZy# zpx@|;b3^97i&UD~vKmfKpNn+ld8ygc)T`}LObxc*!cQ(IcJ(yJtt#z9HAyC@Kx zhR+;O1viLJJK3<8>$Q%TQRboq%Hg?WUE-rcz)Am9&wPc$=i~#f&u*hR!wLR04 zUc?xarb8K?eR)tEfLXV=%|LpCFkHm@(nGSp*>|6oiD{|K0`uv2QZi{59#n%no$q8c2bzoe02@x|y1~K0_M=TSLF9K2 zZ|{7r=q37Qg`<+Tv-+88+n6}26wWSuc^4M}kWW9ik8cp;i852_--%Y>pvY}%$|x%0 z^xB+M^7ofQ*SpvQ2um6`SpdZ*eX;ZX`u+Wnou8PPtZr>_QbbWQ6ab`K-c@BmSoS9K z#eLhpS`?RsAalRfTVC5UAWiuAQ_AKL+A7!p?f{2XKD$q^*B(TlJ^k%%+~8KInS}0A zk=-M2J?(e-114T!)?{35L(tOF(ol9z=sSioGBN2L(vs_+Y4P{;ZP8uk_OQ;@Vdo!% zYd=1SAUr_NUM7!}87)Vo`{`W8udl5Ey;z`oXU1oNVTVE>j#YoUsA9fO z9D~F)8pJ(T5GwIZg_V_BDPEt8T3fTgG+y@d)mM3KK1mj^$jZ?71OcWLnPjA7FADdw zrG4K64pJE#Y`c`NtDuf^WM*ap6uKX6JibmF zI=ZJmK0d#MX-oAV?cWhr(oyA193s7%b`}E=>&p;}Kiyv*8Xi`3cNZQr_vd52+=*=X z8_o0V#fyUGX6fUPySpy|Rl5^aYF@Stm~bcM)z&8MFQeQSV#Oral$nKZbwNeBTLR{u zBL)C&P$ycu7|c(b5;q%_BITATkn~7UNa%eNg&+f|u(WjFzyJap8{71oYba=VUX#hH z>gf%Y8+G3i5Gby!jJMD7<)l$RJUj$Mt^4?K&qoROl8TDofmsaizZky-QvphQiHENi zSSvO{H`6mRR@c`4CYw0l=~N3Ok-%etxe|zqie6VVgEB)wadA~?CV>?!$`&9xflg$K z$InJZqLwqfJ_vz(B6OLD73zSR5pZoIBe6x)(5lF;&qp6=O}2M{Lg;1&i_3bK_jQbn zy5_$}Hk@xYsW~67VpBkdyMJQ@_DZz^qk$Bdo6D)_!VL?0-@~!L)Xz>!yjPQrd5h2F zgWPyeXr&6JUU?Z}$zoQuYMjvbfGPzG-N0H@g?G3;I63*~F0IY^(PrVyDG57!$YbNy z011@bm2_{4fPFx~N1z(JZCg3j3U&K3rBbS^h2ofGe%Z_J-@F8MD%~uZ$p*(#6u{03 zNG|EilLem^bAVU?jpXrT&vXyT*sg`#!b04(Srn*S?R$&8n}4pK1H}Td)gjipOCo#P zqlGmvJ8QASEBey+c~`bvuq;&Bdd3|uy$5GxWaKm|l9GTM6OL9|pyC*%+)`3ViHQ?b zIXK`R$^SJP!((IE6Sx*_ho4<0e5sfTLUi4(Fvt4?56Bjs@q{ELe|2}O4pz9jV;#mOiiu^&SaT{i-!hAm7W!&WD~Xw@j$6uAVvuW(6Rf#kuy^7fWJo^WJUenNbE$qH#i|&3qS=cunfwCh-_874_Z| z6*coHeaZH&J6wS~sro_`AJ_&;I)F$&Bqf=7WE|*oD*>(nnS3oJn-51#fMWfiSFD+4 z{KfVmB!12FMAW>NgLz84Prkl!tZ0aEtZYdJWegO#BI!4&svK^KFNX;vRy`!Wam?b; z8Kun5_2+p}s^*p6zdR9uc|a|jnK7RUz6z9-l3Lx^_;<3q@KOBbE0EhX`iT#KU+K0_ zwuH*o0>q%9q4D+W*MX@ibu%+FeuiJYy##o#PcWA;UW^AHNm6@k-gG`yS64?wDbn7& znG5o`z5V|0OeqyDEtC>7_z7r{r!NHmX6U;BN;^Au+1c3}&^`py51}_5n0d0`xd?ja z)Wd04@LS*Y&-EuODsoQ>e*LiRwqensRnigBGJI61+kgqMviQGoUSkV&?*LIzWZMIz zbej5mRgP%ww5sR{J{orQ#a$nuFhPn2$TG`jZ|B`=1h@%mldG51zDrw>m-i|vD(Z1P zRg_}Mr%z88e#VA0+syXRM+Ul0u^k!AgTUm@ex{*@JZlj#zXw z;is}P!1h;VT_ubro5O2<0ndvKr0jws4Wyp43Z2w&1ZvZ4a`+u9eU1*yvh+dEg9i^F z@afV_qqvGL5JvF^>V=@y7PkLQYcQ&Q^N;;oNs?!!-#e-cYEyZsW75nzY$7OudHEK& zh;O?cxCPUa^$L~jRI`D>7eVse9vJ99Y^bL=BVvL52CWE|WZu*nY8}us=#b?V$6682 zBhSa0E5E&lc`Y-dg?-$Dc?p;a#MowM6SvbQXjj&SE+%zk{&^g^a$u_yidD$cV%3h;jEA-S?aKd zQ+#nsiTq%pMPob^Xn~Q6i;rJFbX=X6d;7e*8#)O_8e*d}lDiTywfn`5&vP{oKv&d| zVgFAn(Lk<@zVa-!M=txK_RRS3e2vnyHuK_M@jBOYr!~dGQpv}cq9>=qXM6S+i&}j; zxBse>iYV#urxRudXTQ22kTI{MiSJ50?{13^mCT&j^jdDsx)47+Cv`=ZM7$*qxpK5L zN$~(SK!k$Y`UeN5qF6fT$gRXXD35#~r=xsV_dC;l9 zrNJOQWu6Cy=|r5?{i1V)aWI?ye|+@=cn&Eom)~oJ4;7Mg(&O$pcj3);t{Dm+qQ_2h z_USv{y6eme=4!A$YOjCmJg{O0jhrK2<%#hsu8PBW`>rAwyBAKW!$U1Qk>{gu zsQMWGjMr~L!FX08$97L_QyL38Vdkvk3rk8JrRigUK@7MR0)8+B<1r~@b!m|0F*$_9 z=Bx+%G3uMxcKAraIf+C^LH&+6|vHaF9;y5$T|f((m~47JG<^*1+tQa0+&vnIk#qep-zsdwv&`a}#2e;Q%V+SGrPYSz8@ zK>?CK>Rt^*THDdE{jilLykMmM_REE{+=~&ZUXEuD$rdcf5bEUUA%pQr*A8E4Hj`R% zl46ZvHEC2lfR8v=qyODcTu-glC5A$1cka5-@=t8 zxsb8+S?DnQ12*VlkZtr6RXFnV6&&<<^r7f}ZiNp8v(Odc1>uTNK12Wonh{JX9Od+P zg{=4SsTix{?_}G=^VGuZ!5LQ0cm-AkEEzHi*zsi4#{GcZN+$dNw-6CmbL?1e6U+*Z zWU4bS%;wrchB!yW_|Ra3vm^ZxMkEK32h8OEn;p;pHdHaeT)X>ry%%cLhA25F>QK)R7Uw35=&%^^gj8;L_Bt#qffARW?iKn`#Sk?sajy5l{5 z?|tw6{r-b@o@bsNvtsWxv)8P><})8P)fMq@sBi!Pz*AOwsSN-iWEBKpVIT_=w=&y* zEq852IiPxsW)Im2voTP%RZ{~vkaa8o9YhSE{v-Lv2BHGc|5FD56%h6R)U`ou|4oAe z0Fm|p+JDm+A6b=Ie1K7>lR$Tj~!hf?PTawS- zdU&{q^YVIod-He;@;JNM@$!p_iShCY@CpcUBPqDueVsfkeYl<6nf^=2|CRI7#vS5j z@8V(a?8NX-uBDZ;r-$UTXa5xa&+%X5^su-6UrkQ#|2-|_1bP2m;pOMy?QP z$}eT#_<#;`d`Gxnwf>uf4-ahQ#=BgbQw&`CQRetci z^y4hom7h72`l~rTJ>3!Ao5Qq7i8^lBBPkl{n{Y5jZe1aU6Y`wUzlc5vgNiP$=*$J+RmK;Tj46dv~J zlZ1Gr)#DNv+%*W^6Tn2czD7~yCyF&~`ErIOGvae{Dg0<~dRqgdhpbg^dkZ~ko-S0~ z)IzMSZ2Q^e}FSkmARq{+`E@CP+q z%a8tr(6M;$U|BE-r+3ZXU2h}!6SNZHKa#5P#A%X1P%;Wq-oKZPXHQ1*EFN4=jn)l? zT0CrIsQI6}`Zq>nqP}D>yC+JR`ka-{Wxf0C^wIIb#7OOZqpgj(C z+j4HerMIivk78(&r*r#wYXH?y<~cKcE!W+^2|vovo8Q_QF*UyJOlaxHFI3&lyOzQ5 zmh@mg(wILuT`WBO3Un*P)Yj-3%B+Oe4jsOZ&v*0)yyi?7umJz}!RAhToFl4W>ZzJ3kIMwR%0ww)Me>ULk(q%;2pR zQ3QGVZ%N6|rv}1B?-RyQL0rFoRpNcEJ%VfxVCmo8ej|KN*A%Jz4om%o;i8kSFiTOT zGQoB*aA{&>MQd~HFv_$#-{NZNtT9Ww;PyVl=jZZ0ze($_Gn3j zZQUkYV+`BgTO2lUA#NSIUkhDw>DJKyZ%W2-El@AA)!!e1U&A36~*h=s9Ga zr~yAiIfA<~NFBca5chs7FpuSz(WzaQ^eosOI6Q4}^!bze6qHOa-!0Y6J0cCgQ>(PM zZuYLOiAIA0O?lU+qte4*IG3SrOq)BJUkudx+?IN6VmrvjOkk-}_HqKiDjTHMUU~7z zcS?bJvLuEgFDoPP{Rf7B^0n%yYSRaK`4wCM{vlNpGku3k*m>*IpYwp^I7ALpqP$EU z+&D^uKPbibhflTtj&Fbf@H_oPX=`Fz%&j%X*HG)9N{o+C>va7@-zO=OpdUBA*!%E$ z6U6;^-~!qc>@l_hX$EkPwQ zb%casJ}DaW#*Fd7;-80!c8`J-{7Nd*O*-h)dsJbWYGc>`BO!>yalCe>j zHN!;*fRNg-?9bu|@TF{wkixGHq$C|Q=F@|!nbT2KR&mTP19=yXKx5l-HO$%hDIbKG`LyA%Y#af#Jks$}-uJbLYV-hiG zU3XgpC2Co6nZiqrNZYK_9RzEvI|}hG*?*nlNnLxkj#Y%Rb-5Mf@Y}@x3Pb?7eV>_k z`fk;Af5F*;cXeR?Nc9w)J31xmDKrHEoW376S&e4C=mA`)*D!!EBX* zo4T4?9u>L702NsI@V8^Y{NW^R6eC|9@pHlN;H!K`&|PT9!Y!_R&W)L^En2Q6w}0Ta z-aDc30UVhF7;8;@sXwg|q}!~R5@qDXM-M&BlLkY^b3ge~z+GMjcl{p7_ON4_?)^J# zR2v?lpY`T;@$qlgX5&m{Ggt&|pDM1^vtV@hB3pi>);So^HgxJ-)U~vO~gAnG8 z)9zRrNhjw<=h}`;Q0>0&5`S8luG;Po8>ZCP7ST8r$FvsR;M@Zdc+|a{pjYNu;Yds?Ewa~gMrF80%O{gC}XuIlKeA~Wgm#^q%tchYp##g@s9MPm8g{9-lm$4;vlz|efofnMkImFTGmZePY| z#<301k_!nmA=uW5s6P_jCz$xC9?p)p+V_H8!7pIQ{wh8+GU#S1FZ#ouR=X#ZLla~h z0MC)BuMNfae1^`l@Gtd)n>jyB1+=MJ-0tje}(gW zn;G^cq~L^$wh?>QgL00%K*_$6Nn6O=$YtF0w4 zoX%C!KSNrIw@yRXQ!Y78slp~3Kgdb7(Jq=>Nn%c~Vge40OnMt{uL6HZ+>;-Ro|V3g zgM?(A zgKaEnobT2w=w83Z3!cQ8TKi1U7a8~@aZ*>2XqZoVfl7r+J~t`2ZG)Wd_yfrE0KZ^y zR}bKOjUh9V6+wg*k1InvkWrFQeSnovJFbJ-Rq^EmElmT;_#?;9J$EG=1$)2)syGtL zC}z8J!WL8b4Ta)H2YCBKj51`gQK&|FuHnzKsr9dVKx?f`*6>z!66?(;_5t`XK7Wu0 z1~Sf(!Yc3?8A?~y#eL=Cfp76SvET86UFxSRf~1cFRYqD@00Va%`Vq+27X2g;~LF92{?3V%9=1Dawp z@zl@1@;T4X5EX0z`640_hI=UTIp{-;(r4`xR6vy{33?QzvLsq$Le#n~X<^G@C+wBG zc&AOi~-8s}rAb`UrrO>`E^``IYYuY^>jXMG7mHJn5emvGAN_6(*I>Khg87Iosv z_^H`M(8P}cN*1IgBSa)DSbLt6CZWgv`gEn@jQrhsPh*6*KM&E1e=U6FQA`55ZY!w^ z(@o#a>}^>d-#|{Y9bM1Uzy390zh<=qdLju$%7J^CE9XyxsRlHOQT=Pi$hIKF1VdYE zYEe!*KNx>(qPiV95sHWnNtc0F?~@3(;eb%NNLXF%s^D_=5kYW#1%LGOC6r>JnJ?6o zRsubv&ol~x=^kRofT)S)Wxs=;xJ7uW@ong3nXmkYrf%_+(|1FX<@fj_>re2%JTCnW zBxg`@r9pKT#7^1DouR2`L4}>Rx?LXoY~yOgDkp`b)OM zSfcO$1_fL3TPq2O#VoRHaRRwTzj-bcFsy-|>Y-zlnfmw1JyrAaj-8R#HKSDI`}k^>5-6g)Lf^QKq7w={uMC7N#8!9v4g|#T(=vnx4fCcpXX2>I>9Dc^K^q*#$HV0k{om# zOA4H5erX?4$naN+VAi?+;?wpC=}^CI_6=}C1$?x77~&Ke8nDYQhUa%30y8u(J=+~) zpUC6mv*#V0y^{P2z@oR<@Sy2%P`q-i24$ttS`poO+4xUQqz9c3PwFk%(yd7-u>Ec7 zz2q4)7eE|mk^tW=;`Ie(P~<&-96Jf@y5h06SY%UFe8V^Rx!|?nC_iD3 zAo;mhIw}Amofl94Xjd=QMSsdJ<3jTVl(?$VhOXI~E#s=D80?!p+wZeEZ~Sr@3^NIz zc=onpiq+!)KJGtO86Ck9Teiaq6_2{CiWPRM?s~X6+M(kUV@0zW(ZBu|`)GP0qaEkK zx^Y2m&d8*q);hUuD@uio!j2#7507IKe{nZ@qC(|K#MuzsXc0ku5qJf(Ki)V;WlzZ{ zw4#g`WjR#0KBw6nzt87WC1q06iDzNPSfxbxD2Ree7C?>sVVZI^e6;Sby=gSyc-(k3 z1|XUbziWdd+i1%QQEwo)iIm9?`;|5L(~H2Hf|+^;@I&88(Bth2K@EB-27_*tNoD|= z8HV5820eY*Lg4M;2L+q^wf+#=ndrUv4c81t*g&N`z8Lcnp_$$Urj&_3<>U}S<&8V~fX;_AhilzeDmT$ye%b+CB(}@gd-HANBCPs0|Euhf6UHob_6qSH= zf?*)RUdHxM+d*~WfVH+ADbWnQihpu50Ik{SZbpV=Kig$oE@mMKn&uj0B~Dd#r0#Mn z^^EG0G{^h=3BDt?eR+Z?tzb_aspcgwW2OB!1=VrvG+#l9go_?iYFXj=*c*hv{H*Uz zj{J)-S%ojk5&;~>EJv||-T`kTo-G5LruaTscw*vizU$-$i~gshj2V5ewj$`_e*wE{ zGP!8q{41@5Ux)o*FDf6D{Y>6Im4F^Oc2;++(OXt!mUq&6zA>@%ku{eB-%YLF z0~7NB&j%TG1`GYr&fCqNF;H*-&PwTa@`3`dLJ4dm-u!@sNAA_45Gwj0-Ey;&X}56= zCgRCm)56ESqMG#1rdzW&xAV7a*g$(FhZ87~=WwHGysL-OKU# zH1842wPg)hu|#d?;!5r2hxTsS+a^_bEp+Oob^6ehPbf{f&Pl;M54=}{F;nqtqMuj0 zA`mC^b87*F*7j9S4H!Vgp5%l1XgFv5G|X*;zdusAQR{N2KAKds`m*mm%(I@3AO@ld^r~<_84mW!+8zw zRTn6Lqe`7RfNkm}_6^^%J*<1f=+DC1MDB+_l<_E~tTIzx1Lj+B;M;9b4>~#8oU%E) z{#4zqX*e%{>i;~(!X>IjB8 zd+}Vx6#5R(YAAj};8{`3Kf|s~t9yB}Vuw;tq(Kxcx6Qi(16rzBam(|||BUyh|0;+z zojv$1-5#uXq%!kTHB+xsZdXy*=l&9I{@|8K+3_S_0D`ON3|M5Qx=G)y5jb#4pIa47 za9I4?7%!jZUWN^-eK>5?FS>YJud1(K&Q3l>l~Bti%~M|{y0IPj7(wmCwtWA`JCc;D zzUwN7I*m^&J@xuHA&91~VqWP0SFGbH29&^WRqF8?5h>E1-a!OhKgYH9@|}n=dqWd= zwPp#?%ENb_R9G3-WB1J{JPXG7X19l^mL69C?~%=4XFAm50n;sP2D6>oXIM}RD}Tdvk)LKXF3{x`haTiO%=*00mWnt(%xFImA!wuJMtYy52f ztgA8RcUC?&$s2{){1io;I+?E~aR_E-)^we1-!I=rvQBX=tcjg#UQKC8OD29fWr``T zt?W#L0pBQxj>o6eCAhPX#~d+!k$Mfg*7#O+xSSv@175@#_gaaM)1wrs-fQd49@jO6 zr#MR_WMop_%sbR@OWjN)kRklH1s(q0?7U?p=rncv!P1l8H)u0^N0OHc17yD8d-`A0 z=i$kPZXvM4yOrfrQVLeRpfl>y7qtnF%LG1Gee`q{<*(-z()K)XsiYyRnbn-k(me8Q z8u7e$Cqo2V(&s;Q`U1Mt9|wf^wuN%wp?cN;~;9iT#n~%8&!K`#AyYjX~Sv zuA(#!rULU(*olpQLU^?37Lkl_zSL<3;;Fy?OHRB-<6pm?oopPZotu}wHe3=u&B$nt z+X%1H2-w#dP&GUGd=!W&sv%*!UzgTBCf!Ut6w@i}+EyAS2KK=(lc9dh?ifF3+~ zY!Rl^i66@B6ZT$;Ht6nnV9qX&){Ops)HzV+cgN*&R+p+lX~(N%%inJO8x1Gqv?q8N z-_Uf-hHri~cj|(7#1Po&g3E@<^vmvDkDn6!nG@OP41`8aFS;N>on_A6{x05Vsn*{bw3&}OFw2|r z;ksTqiVz7S@zV%ANfHSv(1+y0T(lyk#>5Y*j@AgS8eV4wt#a`qkTlxN2d2>FeT(HhB+{0Kc;z$$Q z9j!|`du-?>?6mT5(M*0f!TSAtv3U>@x8ZpcK68|wI)T*15~9Hxo}>&Tn)`U36BZG1wJ-qe3j)f`7+y1lIT7WFLD#T^=67vpg}t7Ftg zM?M>jJqsl=;FkzOJ}dZ&EeT@V>ChJu9(s%U)Mk(>WZH)SL))+b@6bWxbbVr2Ioh{T zIN$4Pm=`WJhEC(!Pc%?zc3v^6fl)COg(pv=PrJov;hz^j%7JzTzJ1=qDt-OKh45qD zBEl_a0WCGx6pNv^=9vBU;Ma!UhU^nNJS{n?x?xEv`s2+_+6Qb6AGzcCnNDG_)mAvC z3rH2lUwI;E%h!#J*u+k|(r9-&oz4%W>nDZ!Z;Ts7k?E5(d&Sec9xBE?;oFAo12Hrl zOAGG|3oP7xRi`$M!=M*9fZd2PL+jOZk3f=!q0f{ z$g(1vihbm*oO34ZCM?@+2mn+i0o5#tJ zx$q~p*7KG&MS(rGx$$0mm?nHm&H)F;p(hS|(umH8`CpSODBYrH=wDu9Yk}i0J1;x_ z;t8^$M1R&38x_*E8LrkMFBRT>k|4m)PJyF`&82I|{*7j*GX-JpPAxm+?sgxiJCEQE zJdXKRXc>`G_{ByD!<04dvw-|bq1u@1hn{AlS@|C5F+FZrJH4?A2T}U^15rEG0-Q}b~|{=oBG0j%(@i1P9k#~1(V6r;{=CQe>4M^ z&qABCxrZhsY(zq9wmG3u;l#M3#QtZYGTfy)T zJ|S9>`w7fwb<@d{hPV(;+DMa_+vp7whXs2swnxtkpTBX={*YNr%ES}zHWU}sM5W>| z$xe;UJ27hg5!(^2B-?MrKl0f;pMuuD>cn5LQ%g*%o!ZwpWx0NcF4`?uT~M8}zb>(# zDD+d|d=p3|py$(9v1uoLYxtjp=(BZ}jn~J@3%jyyI;earrfGEfA_s)b){Axr@GgYk zulnAPLTI>alfU(wYua$Mn`!Q~^|r7ZpGCVm};vpo)T7$;nlY2 z73En@9OTYUrl-3&GJNDZ8fkPo|MTm;sl8_p=t?AWM%HHw@yidhan5I>lbmtgQ_E}3 z*MA^@4<#}ooOCT_%0|A*xVkDZJMVxujeO?ghIXMzj!Eu*s%aLh<9>|h{Q#q@4>Z`S z3+2^gH>Q+k`)ST!T@XLiHEJSZ4!Ay1aU}>b38t<-ByB@mux2KDx{Cqm5 zi~$kg1v4}Q!*Y{|oIch95~uUNT^T0mg#2s?g;Rz!#BlVe!+D-9OFc`5|7(&+os8teEQEq$lDpfR=t`QG|aO` z)ng7W@4U?p)!x~qKG>FL#m6-Lq|;!DI(@)MxL7`~_6}eF7kFM7CDMtNIO4J$Vpw2} zSe00kt&`}A{*mzQ39~!T&YmJcjqPs7lO>hXPOcDeG;t_0pYN})kUJKm@98NOM1bpk z1|^n>hE@S$19zo&%wu@ni4++)t69ruCF{s|$Eg+}bsLuCvRL^nUptW}PL?#DN<%l} zl}XZMC`lzZzFaLO{NahS*2z5v&0zH0RT9Xo-@tf(3QtD0yXhBKZ#1I?y^M2vj!Q50 z+De$1boV=1L5dwLJ{-|~?s;S~1}pO?Hz#K}Ii25+s4w7K?slnBh-5%L{eazR3S_*O zA1anhF9OJKu?&x7g^d_->26(}&9-?S^fJ918y9qEZ0EAa)Z7g=(v;n!AUf{95T_)gR9q`$EQ6&p0xyEePoPe6Q`5!u1eTI&(LpQ zCmoo|y=XW>$hF&bzmGs+_-RDjk3vTsDk}9vxK?{?GJb!1KIt=OF_%jGpbT;0B2BIB zw8!;U8u`CSpf}C0A-WKMuud%K(}#}q111kA0M`E|+4!=u{ryu3DV8yZ`gYgBTu=?e zDMpT2RS)4%kX=h_D+ra=LK2 zW=o`?O&B)dsp_Om$6bl;zdEu3u2dfV=m_QflP={0#4DtccEs??*lmBFZ1hcKB^3=5lp znYaFCEvjgm`2s9f3Nt!rxTq?sQ3VzmW85eCrMw}LmRNQ&lT5uGSWRVRxh61wP)93cQ|$69oc=qVtH8Y{I{GH%R;Hdl;tc8 zjUJYab%kT0vI=|i^_}~-{rPl013e>ubUv#w1Vk@Sk(w#O=cu?9L;-#R$3B|-PD9;Q z7Xqb*vZRF7Wpr=_DO*xW&N%@Jh;?wVO=W1^oj+%;ga_IpfSVI47%wfdv2$Yk8&-Mw z6lr5go7Y)=(WKyio%1va%^jjhIxhU%UmtjMHtX(wVQ$i%;UnofAV2Ofo+8dp9j1+` zNKpPg#iLDRh@V#+oU)RCbCKGA$qVo5&C{+;vUo3D7y z#CK3S`DaNd*3#c%MTtxWPOT=Uhy!UP^9`ctmqNNAuDzY-aIq&?lIGw1BK6}G#g%;z zM*esX3(=+P@8Y3!V$V|Ry%!^7rz-%j?6Fq=ec+6(Nh(bUIT1TDqz#(w5xKo?XqLjM zc*N}PCS=`YDsT<-kK=zWpimz_b)XT6wCkSBLRE6P+==e8ftK#MT2`|=854=%+z5L6y9 zrXumF*olLu1KLzT7)b&E!-;lTA4(hltT41br{>DeYQB9aM|eHV8$=4rgRW}v-J1gi z$?DgeYn0A9inQ|*Tgf^h0&<)lCmK>=6fB+Ks=tq?ij4lU2IfRCi?s!pI|5LiyxkWj zDfVPbqJ)0(SjG2mtTnM94^8~bQq6Pz@5(gr93hk5LS2?Ge&*2`xbfr5W+*T|kMlyN z2cHDZgnw265oxdA(KFz{y>E$(PL%806)apSo+Xp6mC$T3CnSnTtB$jWrgcw6RI_hV z$(e5x`k&{eu2CJ1WAf+`v+HN`ZbjSe2_-lP(Db`fV8>!S%Dv3?e-Mk322V&6S$Zg) zCcK=P3&I^+2o}4V)F80Y{Uw$($&W+BxOTGK@x;ZRVrOfe1Mwnzl8q?0;l_Zyc8hn$ zs`SLQ%+SOJy8yceJP!zdnbHg>zAoNV&2`)4_tK*|igqaGf4=!qSdY#%7dIyM7KwxC zeI)&?L3>Zh8D!my6LD~NI)Nv$NLW1~;$Qe%pp~1&qB9x(?%Wpro7ds|(N2n8j;EgM z5Mv}7=yOP6LqCxyO$b{JINU@ZjKASyq%bp7vr$Zw%p&zeH>LEixZ~^n&;wUvPx}gE z^?O0RI1zmK$%bD)JL^V)1Dn_TH&O7Y{3~kV_iVYB?K|PRG(jG1G9qoH0NU5L(2xsd%B;m*BvbyeQU@*FxwVI@3Z=DQE%eE9X+{TRt!F~qs^@uoGWw;H5u`d7Bx9+vo&|7Ft} z1M<5#_ABKID@*rzHxTvSqh!wAeEL11uz8yBTmMEjM2Oyq!)crQSH^99$Fs7L;&@0; z+(y#At0WKo;K5H_O6=;%QyloNUAE1P?csNBBMN=+wm=m-Q$`dVp)wPDK9S*-Tcm*M zFa^A2?`zustAE?>d`OB{SXqV!9nE{Lem=Y}M4H*+PosaUd#`J%b1&SxDC&OSzY(+1 zTDSS-P`#q>T-j#5bN$5L+*RxX>bG9Z^j;-jV*a(WjgKQ0fKhefY((1;Ct9OsI#J}vO1+vLah@v+2910TKAm)2=7045721K{Z(*REPj^o`mA4B z(aDT9!J{l?t#Q63&HzzU?QCnrjh`vagzHr2I z8+S*ca(WG%)+X_hq2xiYBRwHksr}IY*^~ja+dP8~LDa zjlGieRD+IiXUYA}u!dl+i9`S*bS$?D7{~KKGi|jcZARj64&4Fqq3(NJwEIfm6TSFN z!(vs|U|RxcOR^Ici^@US;q$Xin9Q<}%y*`|@z!i=S5w*rJ>r zg7KI(cIe$3@NeoXIg{`W~8IJ(zxc6;{jO{$Ly_Jz3iyk__7rCM2sS;ZQg7e3R;M zM*3k|$9I}B7Vi2zgjM5kbH5^}ceb*sdhovA6*m0IK(2jkgrvmGrd!Q*ALcAi%&K9w z^>nhNdca1@={{Pp2Xj8vvX?N0J$vXF65Y^?YI{*XS7Q>#pR{j ziKD--M{`CaqkO@3JT{w{ln8Kgd|ZJ6h=>XspK+!&$Kq9w#P5-374p;^s&qCjW2&s} zGu8DwmNKe0^&9aLWZ!?+sb2B)X1;LfeIl;yldN51d|JWJ`$rR_@2fzV04@blN}8*d z5@q!oyJBbcr|p6KEGHwein=nz{^uyWXg5*u`JAGAV=5KlE4L%IPvR8SteP0NO->I+ z+=%Y85Q&Z6SAur zrl-c~;}g5F2M=?6tt5G@iOK_i9C0C)tF`R-}loV4F24W+!bs$9$RX8$9HQ#W1y8` z&9ycYpTwpxtpOTNA3y2tHylr3*Dil#8ka{aMlG$5R}-K7p-GNR<{aHk-hsAlKoX3z z60T@|VNmP5xZ}`K_Z8`VQs7s-Vglh!-nMHUl#?AfuvARY52?0)pvd6^wD4+u;F*fv zpAV_7#lFj=yZf(0V5vm5heB9hpT0Sj=HsnwE<2tj zTogmeVI+j#PF2m&XQrup2xE!wd-O3D;sWW%H6mS=*L@=pBJvk>2lF{h#+$K~3&2DFgWKu%EKD>J6pWDX6VDhZ?M@_yz>i8Crn}j1|45K+HQ=n0 zcAQs+8c-0%p!D3Da-b#psYnmS0!C=a&trD!+`t%Bk1TK=3 zp$>XnD7z{4P0ni%7c(ph$LkjVMe!}fF7uIo`+6sS`A)ScZXkz=j}zsyy)_ci3u(=3 zJS;zOAk-1zD~(=^{zhd0c_`f$W9T<;eDAo7r1U^zhU-%?(p+{dv|9%jG@K6rKuD_m zdknMp51+Oht?T|FeiN~jBL04xknXv|+h#d5;?sqXH$T(BqK#BVKLMX@dTat7W-OL%_+b> zY}_D!XHmFRO*6Q2Ic?6wY_Sd6b09_DgeP)<;Vauurq)-J(;k1H*GW+Ueeddt#!Ij< z!+kga=#vk&?*hKNqg4>f2!GPDPNB?mHZ!rIgN#dTHS?eUA%pV&08yy2{mGgYyN~dN z_Pb^dBX1Zh8gtUn?#w7tebYW7hCw`03`pw9OkRP<7 zqLhi4{D^KSzrC6GYN?O|(T7iHk@TwD9dS-|DlI}UXf)yu`Sw3R{Z~m!v3QCuv zAt)3?Jdna1E*x^AwIGTSz=9rx7=`x=aB16K=^qZ^~*xm&L@1BrNb#? za7c-!5mI7jh_Dn$J@t<`AUo|64r=rjKEqNTtXA<_cMFZ@O>&7T;N$94Q=%LqCfFL> z-$=t|V)FLDN9|T2Odv|{|G@ba3D~q}2TyxAZ-tmqx<|6o+nmqXp+8ZDch+I)YMgs&Q z08R-&Bt*dB(0%v}IDnls3nbGIaN9ds@bHO>3uwXR>|y6( z;?8B~%=%9!|LI4@+}YI0%E86T-j3<2Uz6ANNEdMymaBpO`}5Cpx>&vW-$-`O|4s{- zAkWobc=)(^dH&lsaI4tWRk*s7l{ql-RsRxvVt=>%e_i``95J4&!T%o*^UqBGz6wlL z;)WQ{f5j$oatCkLUmuo`f3ZFJl<2A4V=w_5GvbaclN1pj7#%Bh zt&Z%*y9cD<*BAm@etjUh5vVu^PbFY*koytzVCLE!Ath#Ij$>XuX%fiSt`k zs|Q!_{QqwB<@D#XK79&FKc+lw2` zKS}QRle1u6Uv9xWb?`5f8VfD_?sUnxG>>ae*_g^tMp@+5l3FEj@75gWf-2!@XavK`nmHSy4e)fFMsEp}kw){2Jc_H>{0ptWo1B zSTGk2zlhy9NkP_Gve&1p73G=^YtkezWg(!lX!$xd`A`)578PAlKTgXhk=o_{0ORyoD5FMA9}Q&jS{1|o&l5H6M=yVY}uPgY8pojh7pUt zMr7xi(BXstkcp&(GSt>X)DDi!^3HR$lU0g#x!pQdf)KnNsA* z*1WcSV{wp_Vnh@sOpv@uY?rEXe8&O8HMgi@(uAy2hgI)~NfBykgDSmb+B^VY zL4a0J?Z1jz>U+0|TnT2h+p0$Pb~fA%VYi$PBGwb3H^Dp*LIIUQuFd9o7>y6ZpqC7c zQK!voES&iC<{gX-xzeOgJFqTtsXPP&%*L0&q93$J%@h3_a~6!o_U7Rad3Sj6&C^69 zA}~q_|Z zOeMZP&_EMx!?Nkv%k0%UB*I zWqy90Dht3{n;YP1r^Qc3{#C@6+33w1yW|A#VE#VBWXofu?Yt`fusihFbqi~J4s!8R zO4tFVi@K`XFNs{g&sTWegg|JYc3X67LL?-b(U_gF@Gwd$Z);;lC>_3nu-K8BpR}>? zB&?|Rpd5aw0z>+x)Ci}pe-F5VdgFeQ)ps9A=?Fsb_`-#}^^<7$U)e)bW0V(VhLBBA zKF!WJEqfzThZHjT(WS-cUJUYHF&&DAOPx0UpZirvc-yFuUUR?Tc>M6*2y!tbAYV$x zoqjYF1w#oC8VV6FJJzO*og{0v?c}ijB*jb6UswbBKoJ% zJte=l(@FV->g6cvmJrEmZi;NO8v0g`5s-6q^~ z$uu-F<57^o4#shY+{7;i$5_e`@mN?N|Ow@;zNL<#&$ufK<6J4_&>-;S89~G1{CI)jI z3KOu8?y;}8r;z!Sqan*LMZx4ZyG~08i|OezWc4nIMg;|@2AC( z+@v%{H&Yq+E=oRQcquOtd`W?9<}+r%arx7}H;3>jbRis5plT{=Sn7?F6=a<~4Eg;m zw~?p`5G%gTLKt zQ-0x-g#3q__}`^6hK}5lbbjQ_sRnZ9{^`6aLBD*4djfL)IZCqTf9po0zWnxSKvvxd zeB)mlql`eE##BeI%5GNJ5r&b?_4}&upwmkuE1BrqKNXm$KUY4;`?#K+y;~TB_@H+C-A@lcK{(ZO+ zCd_=%X$)~eHP>z>9D6-mDo$V=97kTV>fg|C`%eeFlz4*LPnS4va4!};q4uPar*LsC5MEIKy~XE11MEMn!qnnQ*$aq9bS3HXVFhIk&jZ>D3x z_AEX3Uu7eMfVqzJPiOPcv)K#h>+ST=9_9c5F328;-YeX%#`-;l8C6TO?#(k|97g|E zFiJ>VLIn;_h`daM`+i4vFX^|7+#_-4QwB|NIQ?=+!5_jeO<_m^N8FVY{2LTyQ7y^U z@kQ4zjga2zFoCQw9b#re9;s}xEkp*le%u{!awb1jdjHRa8^ z;_Jaa98jv`FTPdiJp;T?#U^0h|#-2NSCn1Xo9sY=5#973e zQwkSI*R#(Rj_z3V{@b?<>NFt)K124S7|ocMmb{i*HVLcyu~pVs8Mv)WQ@!J-09yiW zF6_>Ld|7<3_fCaVhb%tMO!AzvE^irBLny z@kTqNVli&VAe2vt9QpEKwyFQ>q|FZS$iqu{^?eS3t7IxDsP3!lXOuviV(@)8A!9eth$2UXFjXYzn7&z3RRsPSWi*7(B+2qd^Co# zbph=jl1Li10i3ca3;6;nJ@C)UEzqhv-on=4wqK>c%;b%LbEd!Dwp=POdWrz5V$OoD zGp2xevYZfAvUg&qBi}&mGo>~GMf^uGNvZm|F)g@~*?Ke$Fsqiu-{YAr>b<=~)_J8T z=j(p^SydGMS9a3>31rP=4Ynq*CX}*nX&8L(SG9y#8ko+lT#_8yi-WJf_;NjAEbz@g zCITa|q?wC|4>LgV0KLiuuKviYGABl$dxzdSmz*r@1q<4d)*N363UGoX1lViBl`ntM zN{xTQnb1KslJ)D3ck=K)?A8CU5V^Uts-KnXIw;$5@R!JglQYh-dcOhhdAh4b1WdA@ z8)V%~?K&5C0q4UJE}k6DNTzs+WuR+j%S=t#{~5&?;yU(GeeBg#8d`hoxpn)!-(Sz5 zDD)0h1+MNyegEc>6hH%vAu;bj8`o%p@^9teg%mKKD^Z^(9sV4toPW~zo_$9-ej1gO zPyNrJyaZm9#%*z_0@Bv)66?*rS>B|m9k^+o*Vvv9a`}UK>>|SaT^sVXyhZ^hGMX8a@fdG2RQd73jwSVzUE|r2nSNg{Xm1iDCRxFL0E}-#T zlb`rrcyfa&I|IqT1^}o4F}UEf#c+52{d|tdT5<=3&ofO@&LgqiW6AY)?IhmBP#r+4 z0s1Ap{bM0FfI{NO*$#fj%YPp>TVh}MF1afJ5fv=Dk-YQ-uED3M7v=>TETUO zMCBUTK%w-bSSs4jP`-dmQ>69nCn#!Wa`95qWT*Q_*s>8TuP4OLy@~E6jZt@2mk^De zLjau?9%t0hR4GFGW9b%4YKZ*kU-YPsq*cwy!8!L=yJ1BO?5-M}nxegDU+61ufo~hk zS2f@Y);;>X43T+y)43f*)M12B;nbSlU!-|P=M_fQXHO?xA){8{stO`VxodF`G8P{3 z_Op)kL7x5Vx1?zjmrWaV_Bm-}KRoYEzoU9ZO+d`xKDT}QV!|TDVEXc?s;IF32s>SH z`NA#WL-HhaCE3dk+2=J=_L}`&I~SOkm6s1v{&5#^Iy?N_y89l~`BN2$xBHK4e$y5z zhhd?Y89pHgaj{P}0NtM1D6F-!BS!RHf3Wq7T4D=<$!l=wsGcDSr34o+=W_^yB5!Ew z(ogh!DQWO)Q}R8r*T~AFkRw_BCa$qG1don>`|YFT^8F&Mi}YPtPc`m+QF{m*pf{AG zHx2%%FAJieb~glvw#Y6!)_O&W?_qDE``Q2#q3VBHr=0!7@f+G@DTgL(UqpPK{Z`|N z7Fw*A&i^`Vs=%Tej&J78im)u*9~1QsaDT}zF;pm*RozCd^;w$akT*zKHnp5Gb~U4w zwej}pU-sHQioGtZyssgzq+cmRa~6)b=mSUNljhwcJLnZ`2^dD1rjSOD>$L@4(Otn* zQCW_1I-`jbRj|LZI$LPS6Id&L_@WOJjYBWQQ<2flAFvBoqk zhkmm6H^8aJfM}?`{DJ1I+dxd=Mn%<%l5M4O^(aZ`ZE(T;e6B?uYqpZwd_uYc^9b~8 z9k+AQN8am%TcS7dwEp!Z=^+j^L0LGjjN=%M8LcrxJ4sy1{O|oms(xLkwlf7CQ#R_% zdC;ox$tyy?*;QjmB>|Xgbd809!cZ)HG^TGY{)K#k@7P`ca25x%Y5UPT??w}SGQuy_ z=9%7ZE`6K!ZG%Qme80Qqv1C_t2h7#3oBzVlvF!7iYI!w!!HG^T6$RQLwZ8GY@;qW% zz#>by*X$t_#nAc1QHvq##gsK`zdvGlNpgp3Ycz)|-`?aFG? z8#Nt3-`&_2Eai>U-#Au%Mo2=ONS)E6>XSD58*eQt4Zlv3R&0V zgLI@yxt6NM&DHn4t4_{%k%V6V7%%FE{t3mPLW2oWQWw|fz;{~=Cd3FbuIZuu2g|eW zdv>mrQjHTpUg^2-hJ`FAs4cl??b@UEe=SD;2C0}>q#e-L4t%!Lf;nKoxA1+19q`P> z;M_1|QfCjZCB&>t7S7Rzu*!WTZzNZ1CCW`f47QoMwZ$+Y?(Q+rE*N73t8<&lpk!p8QWBSgjD|`%2?aU)Ti5c}_F8xv?yolnB=dk5rpGoEuQ7k!$?jZgtTIg-+>+`P>eqzFI6ps8m_;fImk(V^)bb{$wZih zxApGixZF{?+oHgz;_u(@{Oee}$CI=9)IFz>V*=qK%UJ884+VEDiBGw1)Qr>rVOIYM z?zeH-TrC(l0P)YMiQ|UXTZI}NakwJC`n&nF7t2Bk*AjnvEk$d;j5YMMT1c`F51gU? zxmVT_Y$%_m_zJ1?qUdHrIUw_*D)cnez*~B?N|d>9c0TbZ7ppBBe3_^v;N4K%`Y zIq^Di0hg|I`BCW6m$q$?F=Z&C<`y1|0S(8{2b{*4qOERQkSiqxKWyV){;m17t`cAV zr|6miN1aTkFnAL#hRabenEA8qCKP$SH=t>mDoC&gCx;s-6lqWh&~^&nzF4O0@133A z@jYIzDDtr_E4Frwu^pOu-Z!t7OA>Z6q=mc>CzYVN(Z=9qgY38;%MTwCWY>LD5NzFN z3G*)z1;HDkxlh_QZ!Tu!*g0_!M)<3IYU_D7B`9SLl8*Qs2nWq)=?^+okgfKAU6Hsu3^81Y>2Qe|@9XZo;S=0Rx+ z@p4BH_QV-3r!a*5l|oSBiuSEFeF(*nTboX~zh@urHW+0x6%G3kTguYO&ztso-TAPc zbwWX!Ejq2+PRd(Yq3YK6{DVAM#0vPxl5{>q%%?B#!NNr`<(KJdd>@)gl;C@KXRB4w z!YSnehtcd~_z&@7^WVE??iBfHFqHothCZ9T-ls%$M8!m6m6Ti+%E2?O+7UnZ=Dva| z@g(u?8V1}&P_PL%-BM?67w%n+7F(MImY3?wP*0g~Xp5!wQgp8~#HWvZnP3aTJvbxg zGnGmf(&$PkIR3dMVaCtT%`qvV9|Hv$S+c17;ys6&RCb~y z%+~C<(lAr(e*Nwq?_A6^Zb9}E*DEkLwdsL^2+6{vr}%CDmzjP z;{N*sji5S__2aiz*ca9arV0yPZlqM@%lHNByso{3N?phu=Rn3@HJ6GF(3hQ3O)>UC zZ_czwA0rHxnM6SMld~-rJq+~c_f%eQB{oY9w~oV!QGvehl*RVO4w;1fF{TZw?2(W< zF+#*#G>a(6HmD#|EfSe9ja^r&e_G@c>%KgoH)ww5O=8T-RhJQn|NdrzAIsg-<`iYy zW&f3s>47&XLV@f-w3}cmH&^4Mn)R`o?-NFA_cGELkWO1N*eDFE-5Ou00tUq_NP6dU7dfF zF)tlc*D2z=UcObdL$!FKqQ^tvW0S?u(GcO!9q+DWWz-N+w-&;Rn&zjmHL%ti4+@7YGoj)M3 z@if(}sPbsi47-kXsj(9$vE1Y{JjdoX$EK}V((9xT5sQaVu5CzR3OGNZ+(5Tw9eVa@ z&8F}}w~h5^%Hyi`n&GN!<&lx9R_p4+zc1FQURA<0d@@*9yR;*Od^|`vO*fGfPqZ1W zgh6^IFx1jr$ENsAkj*%#Ot45QjPRLmdYal^n-tx~x7hW2bf?bcuXZlpPz690qW{HgsyujUiWl@mLyT(7plu|fZ(E?M@k`t+{D-#}Kv*j>PwzKu%?3zSd<6n5&znVGdD-#(*ii7Q3!)?|>!)1AMR% z-?I;ja1k|-AQ%`OFyu?Aq%|L`DRfGh;no@BAqIaIZ|KbN&h=oRGo8Fs>vD1zbrZK2 zwFL@od#S1}aAyIEVly47fN69}i)1V<3NT~3%$j0B*F9mp?55Z^ltD-LiVj$h*alxS zBtKv@Zx5;ZdEmyAAw>jexMb-3 zCMZ#rJG*~_hrbeb-3nl`g@FDi^h`Q2>J@&U_BgNIa>Dv77>PK7G$zxRxa%}7-6fj6 zhXX?G!7?(f(N%=J++JJ07lcKkuwrTyHna*O*;(nHlgsg z#nR5;%2^L{Y6*=SXt~1Pj+=@+Y4OE!km*bnNYD++i7kbpWU2Ja>WGqEL6!`<^e6Mr z#|BGRZLOl78`^%sf-R-({~$}BYj@dt4|lZMdTJz>PqA&(EHN_?qbxwh{x*|u!YXSt z`qE?1w36Y|1P?kAB=mt)EZLagM6wlcW*-QlPMmdfaTT!Ph&3ggJ?fy{b=31lK>xR6x2pUH7WtMjhD%tR?CV31L zvuj(P6-aBg33^cEp9t+0f2Sx_7onP)@Y?P*0$M{4Qhh4oF*F*8+SGX)ekPc#VGwNi zw(SrHh#+tJhkn<5$B0x~!`4))M3%J66~0PnD?OlywIeqEQQ2{%5};BA;zzY4(&a}` zsjCQ_Xo^H@>I|HokVq|M51o#%GEHfor*k#2&y?HEl=go0SmLUphnS5`1)|K{k{IV- zbd`QvmrW6no#_L0f5JMP{INhZmBsLklJ4Hf=~wrR8~?`YE>azQ`)v?~n`;T&3E$As7OniC_qR zsv!1~BuFYJ*u-!t!=D~!=r)b~e5R04CEmrw=+9mI%&63PQ+1g7AUrE9xqautiSmm$ z|LuW?dJk0fp2|5{P1l)Ee@fJzaY~Q|WV&3Ol4njnGYFEmD&&TGudS{FA$eV$U%p~i zQQ?D~i+PfCLA5PjYaeWy79%dFSF3fYhY8~>TpBy5p0u|hR>q*+7 z>77FX8V$p>?MCw)1K}h3`Y_ymI1)0AK7?W<#99wG0zvX-TtvC_ zAabn4bXeV@Kq_l}7{gva@hkx)ZIjAMh3&Sbu#wZ53&WC`iXJ0DXj?;N`$S1zR*s6N zM7E)`W~{^*5H#$7$l7k#dw!Qn34L^QR(<>VoWRxDQcFv@5t z%IJ_^HjEzBqskH1)g%k_=3rZ9)ORO}*31B4D4NX6la{&G%4>wl4GoaEp=q|AnjR+9 zbWl$HV5w*9;85vFq8$`Gk#0+jaMk7sXMP$cRl`zXZ&(}Nt(r5_0Pn843)r>|(Q&oj z(t1LwRP4h}^2Nc_?mao52<6i|tF|eOpW%r{6a=L&w5>UeIORoSR}Ib#B*`G(xlF@~ z^K6uFFg`DDr#l$T(n3a65T;fuBGm%JP>clh4WC`T+L=b0+?+uC4g$Oa*|M{@)E?X7 zRyBOOm9IfTc!b=u+Fa+FyAUVW2wSr*53iWctR)YeuAK;pvBiWg?K#%+WkY*W-7?EE z+rThIR+Ah?6x3ANmpsGw<8VTq9M1PF>nAX5S!P5Z*TdnGC3he~5_~%pY%yBd-FS!K zmn2q&_FFKwt{yg4+qQDeY5I)Pvi4eWr{Sttzkc)7@G-rvd1HR9B%t7DrWKBAL^J_t zNdovBity;*Oc^F47w|&NO6M>xhUiJDCkgMqXrl)x=!~gX$ z|C|yYa>s+FC$AuznD%*y;#dO7^U>2K7ACrMU2~!J=s@;m&96FLUBd#_lgoXq{bOt* z5(81odJia7t;Mr+TMbYZ z5&OtklzB|Z)dM#2D4N{a+$={sR4gZPJ+Y7xJi$_-S?<%l;`soUtK9Y7EhCwGe@iDmO0%ziSMG)qRX=1uA4?_<8u;JPsnP>=K52pJzS4^hxU$%vi$5DX>EWXb^U zj0d^i+BBPA{4xrNIDk9;GESjRE$}mHGt*E*&1zUSz897efrQgTu=Jpb$FPn2UNNh- z^2&6sAS@-VT@noS8=E;^=JF*oK=f8P^r=d8nqhX1}6u=BeWe&8|D2`$j~P%Krir~Z}gCqQ3SPL z{P(czj*k)w7M&aWo&fpO^a@h5HZ6&hxt@m0IVQIx; zRHoCzkd((X1ZQD?0sBFS0n2PP$r7($OgT{sDI#LCvZ9pc8QHSRe)c6F`>^y}hXQ+1>EeqR{YAx%<^`8mB4{S9j3ehx0?pkV^dy)G>7;PgTWYggN z*mXybGGI@}IOpY)io&0`tH{0&iqQIt_)Yq8hqB+|=ko0Hs~n7(v#%LRdrZmXI2@~o z43CMVP8sT9{!*qKOk{u@s4O|o4UAa`=63~cf@BU`BLODIptl8WepLA5`zoj$rbb^b z(jdJ0!sz>+ALPA$y;)Kr)c@u&_vBzCH-k&2r?wkpw2l+vLdt{{TAsvqOz4#=Ig5q2 z+AW&L8jSL@eytQV5TXZ!40xs_aTA9%bs?ZF%mJ{=2Vnt~WhG<*hkLhwQOVN}IB@1Z z846W&X6iR5wxRJb>(bY>GKdP0^pK`&_c0CCq0KYJ zqP3;N3!@2nWY(_k{7#~HF=-40=v4Tm?~gPcZOb` zjVtc^xNH95wuky3Rv3Fml$oGoHY$3#PNKuS2h-p^vfZTLWqnryufm7l*f)*Id_T$w zQ>CgHhsfT$FlBvrApe^rL@^o@pTx|j}cT?G2 zd(Ezivz!D2yr!LW_j61kr)4yCGCSi}MJ_l0Oowmd$4rRFvEvWmnW zcpE99rIXfcJ)cUWR zV&vQ-@HyMk2N!35aAo^-6?9C-(d0YFe1xS%IQuW&rQO zFOu%J&H_POK?m)uPr?$ht~L6QMei;!!s~5Hx&FG;Vu;6{G_}DBB~lo4$q4$!-v9u9 zyG`bnAW;&LnI{R5+>U+pNB-ZE@?x1!f0)s zA<*rCj$NYv)Q`T2M@!0E@sWxi!Wx#V^RtTA;}hcF`N{bMi}gHi?M&_Mm*1cVem+~9 z+MqQvO61c1?vp)BVhj`k&A<(vVV`@)FTQvg=1g4=?1Urr9|otom0E9d2%=qk_b@yS z?R2=s{oPJ*oGf=%S2^yAto(`N8UB9zPhMe7?%m=wV>R~OatYhIJc%WXFHiPX`%wJX zGrX^oIyze~*FPT`k2O&LFjIo70=%&18stKdK)#}7?;Fm_H;`f&vxU(}LAA1-of?Gy z=!COsU*v_YYeu1#(f6T`y^I>%awPvBAy^S31jt^JD-E2bw=XX8pE%=&tu`dtC$kI= zI6q=a9b@YO(-CE?G1G6Pg}6Yw%ShQLWW7Ezh=M}C{}2KEX+o24VGQmz=9aepa|0mY z@~U$a0XsUhdFTAB>`198XGn4axx@Ghcq&ZK94IZzbS6b2MHxRjYfTPOZ(_z-cVQ^O zgr#+j^A-1ga`A$+`~#FoP<2phbq>dg#my_A`tY@IC*2j)a8yzBV$2W-@&PZFd2i64 zy9P+VoD5z^NznVA{Qi7{2rs71^mca58}MApT?V?E339$`Zx@0udW#srt%-$o6r!8O z(_6>GZ=%fAJ-?3Tt%S>8(e7O(o)qxqx^^Qr$kHyNZs_;bYMuyUhon6wt}^q^*S^sG z@Pgd6dS-Ut+p8I*ng!uFc zcQRqkP}25rT}0P>h^qP6ATx3#%gF#f0se8Z-$lpQqnZPa{TJWaCZBjr&=|N3_*Q|w zBgSNs)F3|W$i;F(hl{;7A=PzPgw5>fZ{{!9I44gA7o6wIOKzXS6bkTG&F`Q#7)p!o zNNFp1KdN(Jv{T-o-vLxd`0S~}i3{ z%#}~GL&W-+9r&L1PiaL6__$xKQnIS9S2L67dUhIZ@*umgz^;iBaekCM!+^r%>MG0I zUq9AV?Xf|g#7rzdzIvA*u!uic11O_WI#*Y37|IoN!eY_EaKA=3LqcxA6f4L$_nw#7 zz^UVY&l9PJYn0)1@-tA3@Mig>GSSz|pg;QuGv?G4yE_4)EgrUyi|Fgkwod_CGT;&y_-+eyST_CT z2fz(FXvNQdp+?>%G!O;3r?fCO2=AV^r;CyKUcP_-1vh|!a^&X68C}(P^dmTzmUV4XSZ|LjwhU;}brYa^ZR^cB@S0?=2 z_AyAVWxmWfuMMoxTm!q8vqG0!OQV3dzX^Gom0;B4{jC~61}nYosr7BJ~wU} zW*fg~Fel8>gQYm&K_iSVlm&MpV(GmN>OvSl3zRd1E=!3`pY;)Tni9wPF zU;FqCmR>iTcwa4NN{?$J=p!MDrq_7*eX7f?!28p0!^Xl$>WI=6EpKh&GQC$P#G)c)li<>1;N^AQ6wc=bN)G#nv@m%6@4~Rn162d>BY}@*$5`j3;NqE1r0p zqMHo+!y&D<^2^Bwy`n|Wzlm_HJNuqoiL>GJ(FXJJ%*bMJ*9{J;XHGzot&mUl7@=hyXEZh0bn!{f;4^p+`j9z?iv71Bm#e~&{P+%xEoMI zu3&H0#O1A#p1;aA^4Bi6zn*EudicI}~IK(vBKwX1Y(i z`mitl@!IZspRJv9f}EDowZs=;r`mytw+9VeiiIxd?1mzV11=h1q{*41%APx%6FI}V zCuuO}@&`qp198rW7Bb$kW?%c5v&pmauJSVLe1?joXRED#7j1r8HjOPmRRREBP1_z$ zBe%E~Q z=d+FQwe-lN3+7jE^(v;`*t^$kejU|(Gq8eJAo2dvZIXDkXj#+oEwP1udm%J~ls-`NQq}YY6;sSNEtU}Ss?@q1DN#)C-sHxU&fw?~S-;{+hqs;Lm{R#%0bQ{>24lvZdaqli^_c1{ z(>8#zg+Tl?Exy8%(5sNLN$nh`VR#%UarkZh)1$)iowt6;*F}Y%H-7}GB#-ZUuekYI zG__XFJ*E#I&pg-yl8v_d4B~2cA|QaL_E%CpiUfMlePpPa`#O^B8L1QP#;*W3|{~6I3R_G zV4n@-kH6;jN}M1k$*Gb9M=%rB*lZ1uoc?k6Lg;~k!>*xb5yx^e!ZU0$Me1)1uuka3-PaN%BRi&S}K2g)^jMG8|p zwZvW9b0&f^c{uG)Wv%bickHeHWZ414ZwJ&V?8r)T)je>e^rK*sbwrv^Jg)qbh8*ci4p;X$M6TT|8UnUfulSv=()fW~Iytg7 zS9Vu6W7BXYY3@<2tgq4E7M7eojd3-7CpOWA~7n2tLRG!xr!m@)n6y~|uC zHFzniGOzqTH7=O=RY30AtPiyhnI4qg8c*)byu)Os-pqliq505k` z2=<>rJ*L^auOHssRG|%5k5o`(tdlvh^V!f7vN5{ckNpETh8Eu+{Qg;^BBuxIexJ^~ zIVE!*Sz`!s$oAMmV;DguG@G*6H>@KSI6-WEGciSY^-{cbVa_Ga2w!1xe}~uZ%CIMW zYl{{Vk)t|2v(Aroy8q(;p?O!8Z0#P4XJ_9d#kz!xbawHa395eTE{>G0JSiqD$8xQ1 z08K(Kizb}72J7_4n}j!Je9dc~I_1?Kn8~nyt5iGRXxMP@10jm_S6dqi)+cc%n^ORC zl{5KygY?jU1$4qh$++}m^?OKsFf;TdPWwr}jD>?WN|W(mKFq3+fffon=3qQbB^KBe zE00?ET$5--51!z|mZ!g{v#Q^UVtJhZ*fe$jc{mp?=3_$G3>MrD@0*&A)GZ zs9~T+tQRQ{Lu%nxTO957lt#h717xV{w_bTa46%|h*iJ0vrx0UABFgfR(DO3 zBist5>f23XT@Wi0%-Rnj41W!7H?wT%xcWiN0)$O=`YQB4VYO=Xw?<=FFt2{)uqAq~ z=k&Z<|0PkN-epB+yX0=im$eJLu405gYrVTmjKY4QQwOq?d*iv|nqtljP?p_KP-=OO zW+`%YyLUn5ak0Hx^65`Zl3WHAM-~9ATi@^EDXJr7a(S5$V^iAwa%`aW@wuq0L1>aP zcXhhwrYYm3*dNKh5lO+JJLsbTHcuM0UFN(oyq!mv-r1!ejU{UiPti?Q5s;fRBLT3R z=|$vk>Lh)S1<2vuIY$l37@**p%l^40Ixtr8Q&tQkE?Zt;^YWpwjIFrGglY2VS$j-x zum`qG}_rIq*{Av_pOS7;c=>WcF9PDnvv_^>H=aWe&1yWjtf> zX1{-^8?c8r@~Pzb?1Cckw9tbDSyaidI1daV!?WoHhGWatzr-VYYHhGDc*JexuTyvi zEq>J`nSus6t0q9l$gfnhsp|O-gYTBLk+u8meoU2CUFMTMTa=5_168*Ba9Aq(JX)U7 z$K}8rVd)A&n0PR^NODzGy#K5zDt}oCKdD(y<6f}_pg@bL5Cek_m6#tGfxOYjfIm0JU2T8s@%vaszJJ(% z_b{G*utuk|JeVO7wD)<6I=*2WS!5k=YUdbt8{ih1=6!Dljq}i{6mE+| zK8c6Uldk4J@U6mbd+0&I$1#6Z+?^2#qD!%(uQ3x`cNjJZ}5#` z%9_3GsWw8xqB-vv;bp|`v^Oxzox#muL^3I1OhF;8u-avKv~gtdVpliLVAh(DcQBP zp?Vn!%l&sWb*U5A=05#0@rArE*p!J^+s;jByro=72$oprk-T(F*eoAXv$mrzoV5JU z{{l!@vSCknWV?~zo-V(nC5H>|ErVZ7GCH@VT&|^TUrxI(I{N51V1;!l*$WBeEa*oerbeV>*Dn^ZYzS}1*Jb-T_AQ`qLi!LN$@4s5cuad>W+`XPR`uLK%I7vF#xYXS?BH-s#L69`D;1E?ll6 zeVE?1+r6{5V<{IMDg5gDkJna5LEZ6UN7Q8R2b%}w1+yo5SlaUZ#kPRR^`h;THjT>T zz1uC^Wlv-w8W%%OH&r$?5^@vwWnp}?u{@o^e&PgkZIAr)|1>TO6yQ7pD?6&>)uSt(Yo2kw+f=vot{LuAL^eK+B zAp^O}p^Dq*_#!;6F_-b;C!XES!eR#fn0BHRmjiDIbxu2^=S&?oGo zzR=bC!=YoY^`5=6?tl3%2>GT}7X=3PA;Kc1yPFac+*4PcE`A1fbz1J0P)nViv`IF2 z{w6gVR|!%How-h_^k=XkPMPi^LU9YKj%L%z@NsY6IP`>I{=BDF%Wl~Hz@ydYv+bP6 zF0mKfBkub7!-hzgh|A&6MhbPCYre}w5*G)xjpwzhxB93=RIWJIQ02}xp3r+;GbL97 z*nZ4U4Gj?ICg`k^@>iKv%xI=r<2+RHp2AzOTtpk4M!$Kz$SSA*LAacivZUKlW42W~ zX>+E91}R&uUz@o^HP_);nC-!I9P7Ilo69bF+TrZCuI<`YRUURjol#;9Q+W6s&-x3U z*m%z4l9_-7#}(STg{d6x{aEQ0A}A*XNn1rI(0tHgyXn60{Tn*n_W82j%Hax!x`z^O zFt)+vSUup-Zxty2(h=m;I~_%}TE@m@8G6cSKh(*ZCfO}!JieGX@*4;-)DqvaQnzmC z`Dt5xv(GYX>dv|ySKmNp2)vc)pqcUVH>2PUev8G5ukc&IVyLu4aJBu^uKRH&e7;$h zz~fb6Tb4!rECf6H@c(f2m0?kJ-`jK;gb1jVfPhFN9fL}Vlyoy7-I7v6SV)7U)JVtB zodZaB4qeg=9YYNDKjZUzzr5EqpX7MXK5MVN&sz7o*S)!DquGUtTZ1TCxKN1y(Xc;StHlU}3k^tzt|>{7-)773w~$d4bVcWp0Y7^b7)(UEIYiy|t_ zO@|E}Cg!N4{Ae>2jo=oz-ky%Uo{H20`1>?T!uX!juWs$08t3j3RhrImPd96y?(meQ zruc@(upuufQl%~{r0i%7tEb(I&t7MLbr*|JhgYT?ja6G4UPJwFE(R7oTrb1x{+Nmb zYqONjW8s>JG$=H;q!lc7RgBi;UW){_GB(zeS6_mxaJp|>NUvU@20y_o>R0IYbs<}d zVA}=r16OSJN+ixTG{q=Dol6=iAN$7vjh~gH@K3xUN1*{nj8<4^;r|kJUPG$M)ZI*mY zRIdYjqwSYi{zc;2KBK+StAy5np~eC`SXmqhEoLtjl z&j``=#1Q|6Rli%++Y?n`hQ|8K`TYgXf+f~_8k}GAN7W$;ptXGs%bk~m2Lt}_!JfI6 zNm06LOxQDPwr#nsc6M7gIfxr*uMp3gs+S&}HusEjySuA=CRiW^;DAF>g;rW6-UwJy zk*2ZD*L5DkJ7pcC<0|C-0}|sGZH>+lRU)aH4hCC z?8sYWBtNkT;Kd@X;DQBu2m2u8b zX$Vm|Bx~?4mlW6iT=zhj(H|!w`(F;(s}C9tnhvf#RO}`1g4b3b%0fn1v#Y%hPJz~d zPbg~tL+XK)-%j6U^fq!aMOB+wVIQ|An?qiMu1N}t{?sWd4@LjF;q>;j2j{^+4!C|* zt?fNREKYd7XP^8(p?)SbW_bg6@3f)X;qM1Ecvm?sAiQs`?VyfoO%;m|qU+H*;w(n9 zGFE*l9Z(0?;wj+Y&vf65o6{u1>-xPjdCs0L7vipN;Sz@^vwpDKi0nXuh)tjW zxyBpc8K9UlR_%1USzNcCw0UW(X0B9@)|)tihYIgp6m> zYhm(8SlVHjl>h|~1-1B-+0&BGqWvzl*=(<3H9#F=y{kx<%I7ap5*tr8XjRWwe%$=w zxq9Qua-AuK&KyvA<=EHE!&X%i6lYzq;CcP2jk<^}v31S|VDQ{<+?JR=QvdU|s{EdH37CkoHU_juD1V?^#D?Fgi3f;F-k^D_~3_m8bhx~WmGH(vJ-`JGlE zFy^*wUGWpX{-tkXbuU-_>|7Ld(-M^W{AT2*!F$@8>dO@`<3FTy4({V6Oq==s3-3iR zI9*Txspu-1|51(9l)eF6KQu*CUcKn`b=AEe$LL>vuToufCca=9#5GpS3>#o8ttzS9 zQL--M^j2EQVyidL>)VP7Q(ygH?EQ`%UFY7q9e4$MdcEO~_?6=I=x$fQ5b?^rQWF&} zqQyDqd=;Bs3g?ImA$X-D(xMZa;plGx)HT>OWMAWI4SZwaS^_-|6QfoBIB10WWNJ`8 z#5r|}z=o!wZwD5y1}qOZUhDhs`%_isc5V9`{Yq1|5@Vt_S@v7UWc$d|nr6uH9zO*& z#dlWTY|)NojTdQ}tX!c^X`1zKr<;~u+?KYH!1nxaFNI0(9@S|%k{*D!cW`(`SBMV0 zH1^z8{z|j`c^U*o#;$w5tJGwz;&2(xd8B(nDtR)@TE2PA$nYm7?Ia5r1;meDV(sd0 z0Yf2ruYUF5UD@EF+sHeyjCpe&S}c!VtTa~RIBERTcUBb^Vk)6z&ne?AdL%s2F~l&dT*^WD^{mo4xkCW`mw-4m7WaQ-lSEV1u31oPWJ^K&}G&aEBj!HYNe z%FHF)Q-w%yD;RPpUsfxqj&2Q1zcN}x84bj_N-2EiMh{BPAeERVpxQn+}RP?u7`m^GUlQq2U#7h%|`&`9lVvW(v)8(CmNjQ(+VQ8}BRs+5f#O>PSDG&>b@62sK*So6I z8N7C-f4F?PB=NRO(nDG1d59N#MCBYF^)`$2iv@K_iSgh_%}Q2{^Vf1>?x;>7ZvVjy zBkbx@AbXG0VYr;?O7h7Bn(L@Nst)U^#fdqwWT?UzjRm(JjcF1x)%Rv=p2s88<>{x> z!~HefITtv6=W<%scL)wYHO@`4JvgslwaHD@p1&CU-`Fgf_zLBwQYF!BWg`5n>kKol?k$KsGecX?6Cs!L% z&ECBlX`{nc=cr#lTw)fAj~?@@k$SzE@mt+ezh81bjkfySE@}d#=+B@&dvyp5{JcJv zCn50&mqF3=wP_GmP=N6B%J(vG_f?cOE@aX!K;$_&4V{&Ne|zRbA2cZ!7jYxljI+z= zpG4g4I>caMhzjVnz0?Ucc9PW4v-Tv>Uz1q7SpEL|O8P&AanGlS{cZo84g5M0oPLgv zwH9}Z&$6c4H>zAfEZP-g$J0e^vr3%7r&s*+~F!F>9?ge^F7EDofW!i6Qh-?KDYtIYt02| zrE+x8XkZWPETr{|Frnv9bSA^Kbqtc;Wrxt9 zr;%Y0CsT)H?WuKZ@YV6U`@Q2{MU_W+DIcDp>($W|)ZSG5j?y;i8&z5mTjA5KaSc@m zc@eDFh!P$uT9Fqg{olcxj`(XsmW$zs>{ulz;Ox|ZsKFYE{1;)b3|1vH;z205C zai}=2aE;shE}(S7ha(7JH@;8I$xZ&Os($h0J?K&Ob2)YtD3Rg2SZB@5dp6PKyQ(L! zDDMQc-7cTJ7RVm14N2fUi_#02B4e^5`h-d;@e=*C{Hp-*}v4K$NwGcje=>bsg>+8zdhT43l?#`RM1Gj2=kAv#JemQyZjsB2KQu*>Ai$C_V zH{16GY%x|=x@2?i&R?Mk)tgmSA=1OosVN5Mv|+Dg0GgFd+l3_2$BrRMr{XMnU_A?+ zbpn++!o3Z=tQgCbM4%{tlof^iEhZnMI}Ynn706!ReK+P2#r9_MPngCl6Y{^Hm!y$p zO@$8rDLBRFdbMxs)YlNwj+qA^|6(#hOTzRb9;F2gXzwbO{4F}*{tu;P8FV{Sem$$k zZB|!Gyn7hC(NWfNaHRk=rmO64LClSY>g^B1DvI=xPZ3{gl$RS$^ZnxVhA-eCl1)iq zceO0ruubzhtIQP|Q62tV(g8Z9NG2Bn;kcK<8TYPYH=}b^LDh($W**Y(7{{KxdpB08 z?%Mw<2GbWU(GTdC-EHxhfSF}_GPoavb(bba^sK>4E992MAAn^`H(q!>jb!X#%0bcc}%C7Zs0ibWBC?L^MQ*<|5b9rRfFC zsj=AtH%O+O)BWPfBFmJE-LAw2^W-akf(~D-)mjpBt~R>#2uV5_Y&xZ~+XV*6Ju59O z8YiZheZ9zx{3;Mvqc_pOS?m;N3mtz+xld}1)(Lut-XwhS=IwLKS zn!#HyjHn(GK=B*qNhL@nY`!MZqDoH~3KO6uI5N3ke2{T>W9jO6$jOmoiMIu}#N+X5 zVjd4f!!&(GO)ONxVev5HlfB0e5h)R4DuzLd8?)th3RQc@)KX8^MvAb|P`dq@Drlq`gbu>>UjJqF4@`S<#qM(m zMLe19*{{K>9@}%Jt77${_q+Teb^c{N^3(fv^P*r+?vA^6Y?xw`D(B>Q4&PbE3m}

    XEl1PdeR(&W>e!RycY+^z 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 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-32@1x.png b/Assets.xcassets/RimeIcon.appiconset/rime-32@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..751741527553a5fa6667d94db6ded2ac708ee8c7 GIT binary patch literal 2289 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-512.png b/Assets.xcassets/RimeIcon.appiconset/rime-512.png deleted file mode 100644 index 8f3aec506067b32a8ded8e83d0bd8bfdb6921c27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17162 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-512@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bec059c5078629fcd7c47e139c9f632322176f77 GIT binary patch literal 117507 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

    @1~k#8ZxCD1B! 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

    lUeD1?~(kK)^9z0qKJ%oyGD>cAj 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 AR{#J2 diff --git a/rime.pdf b/rime.pdf index f285977dabb77c56e81c799ef2957abeeb0f6701..8cac7ddaf2e0e9e72392329291c2616185e91ec7 100644 GIT binary patch delta 256629 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�ArZ@s07rROq2Hv$ZJ;rNQ-!XZqpI`2ht62_WQz< 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�BGuW$TXW)W#N9tOC{w72(OK2A9 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ⅅ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

      -&30LuDpyFI9=)$0L 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 delta 256243 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

    1. sqs%xSblx&m_RbQ;=G5Uup8~uav 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�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�K7F>Z1C=KBKn&Hb(BU=xhKR8J-n6256~K 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

      MJsVF6#rHFeXG1OYgbk(jR%K^Z} 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 VF6I_^Pb&|B3gIy`%c{xY{XfR6Nb~>z From 03f2886228c57b09043bd9fef7c49c921c5feb71 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 2 Mar 2023 12:11:48 +0100 Subject: [PATCH 030/164] revert NSTextView --- SquirrelPanel.m | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index f61b0b5ce..1fa43309c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -231,6 +231,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) CAShapeLayer *shape; - (BOOL)isFlipped; +@property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; - (void)setText:(NSAttributedString *)text; - (void)drawViewWith:(NSRange)hilightedRange preeditRange:(NSRange)preeditRange @@ -331,8 +332,8 @@ - (void)drawViewWith:(NSRange)hilightedRange NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) return path; - NSPoint previousPoint = [vertex[vertex.count-1] pointValue]; - NSPoint point = [vertex[0] pointValue]; + NSPoint previousPoint = (vertex[vertex.count-1]).pointValue; + NSPoint point = (vertex[0]).pointValue; NSPoint nextPoint; NSPoint control1; NSPoint control2; @@ -345,9 +346,9 @@ - (void)drawViewWith:(NSRange)hilightedRange } [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; i += 1) { - previousPoint = [vertex[(vertex.count+i-1)%vertex.count] pointValue]; - point = [vertex[i] pointValue]; - nextPoint = [vertex[(i+1)%vertex.count] pointValue]; + previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; + point = (vertex[i]).pointValue; + nextPoint = (vertex[(i+1)%vertex.count]).pointValue; target = point; control1 = point; diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); @@ -386,10 +387,10 @@ - (void)drawViewWith:(NSRange)hilightedRange void xyTranslation(NSMutableArray *shape, NSPoint direction) { for (NSUInteger i = 0; i < shape.count; i += 1) { - NSPoint point = [shape[i] pointValue]; + NSPoint point = (shape[i]).pointValue; point.x += direction.x; point.y += direction.y; - [shape replaceObjectAtIndex:i withObject:@(point)]; + shape[i] = @(point); } } @@ -493,7 +494,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe // If the point is outside the innerBox, will extend to reach the outerBox void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerBorder) { for (NSUInteger i = 0; i < vertex.count; i += 1){ - NSPoint point = [vertex[i] pointValue]; + NSPoint point = (vertex[i]).pointValue; if (point.x < innerBorder.origin.x) { point.x = outerBorder.origin.x; } else if (point.x > innerBorder.origin.x+innerBorder.size.width) { @@ -505,6 +506,7 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB point.y = outerBorder.origin.y+outerBorder.size.height; } [vertex replaceObjectAtIndex:i withObject:@(point)]; + vertex[i] = @(point); } } From 41392413af45be1738f74a63fe07993c1c39fbbb Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 5 Mar 2023 06:23:35 +0100 Subject: [PATCH 031/164] Add symbol before deploy --- Base.lproj/MainMenu.xib | 2 +- zh-HK.lproj/MainMenu.xib | 10 +++++----- zh-Hans.lproj/MainMenu.xib | 10 +++++----- zh-Hant.lproj/MainMenu.xib | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index 8b1464468..29871b6cd 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -21,7 +21,7 @@

      - + diff --git a/zh-HK.lproj/MainMenu.xib b/zh-HK.lproj/MainMenu.xib index 722c6e1b2..25bbc320a 100644 --- a/zh-HK.lproj/MainMenu.xib +++ b/zh-HK.lproj/MainMenu.xib @@ -1,8 +1,7 @@ - + - - + @@ -11,7 +10,7 @@ - + @@ -22,7 +21,7 @@ - + @@ -53,6 +52,7 @@ + diff --git a/zh-Hans.lproj/MainMenu.xib b/zh-Hans.lproj/MainMenu.xib index d7ccc616c..3e9cb3c9e 100644 --- a/zh-Hans.lproj/MainMenu.xib +++ b/zh-Hans.lproj/MainMenu.xib @@ -1,8 +1,7 @@ - + - - + @@ -11,7 +10,7 @@ - + @@ -22,7 +21,7 @@ - + @@ -53,6 +52,7 @@ + diff --git a/zh-Hant.lproj/MainMenu.xib b/zh-Hant.lproj/MainMenu.xib index 829e72be4..e90fa9a5d 100644 --- a/zh-Hant.lproj/MainMenu.xib +++ b/zh-Hant.lproj/MainMenu.xib @@ -1,8 +1,7 @@ - + - - + @@ -11,7 +10,7 @@ - + @@ -22,7 +21,7 @@ - + @@ -53,6 +52,7 @@ + From e54a617c8a2299f1549d56b03f2cf3dc5fa8a5cd Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 5 Mar 2023 06:26:01 +0100 Subject: [PATCH 032/164] Add Fn as a modifier key --- SquirrelInputController.m | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 94d4c88bd..1f090362e 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -157,8 +157,12 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender keyCode, keyChars.UTF8String[0], modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { - // revert non-modifier function keys' FunctionKeyMask (ForwardDelete, Navigation Keys, F1..F19) - int rime_modifiers = osx_modifiers_to_rime_modifiers((keyCode == 0x40 || keyCode == 0x47 || keyCode == 0x4f || keyCode == 0x50 || keyCode >= 0x60 ) ? modifiers ^= OSX_FN_MASK : modifiers); + int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) + if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || + keyCode == 0x47 || keyCode == 0x40) { + rime_modifiers ^= kHyperMask; + } handled = [self processKey:rime_keycode modifiers:rime_modifiers]; [self rimeUpdate]; } From e84d0ef45ae99e16bdd002ad219976451b1c3d74 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 21 Mar 2023 18:24:24 +0100 Subject: [PATCH 033/164] expand ascii to latin1 --- SquirrelInputController.m | 58 +++++++++++++++++++++------------------ librime | 2 +- macos_keycode.h | 21 +++++++++----- macos_keycode.m | 27 +++++++++--------- 4 files changed, 60 insertions(+), 48 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 1f090362e..640a1a028 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -146,30 +146,30 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender int keyCode = event.keyCode; NSString* keyChars = event.charactersIgnoringModifiers; - if (!isalpha(keyChars.UTF8String[0])) { + if ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) { keyChars = event.characters; } //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents - int rime_keycode = osx_keycode_to_rime_keycode( - keyCode, keyChars.UTF8String[0], modifiers & OSX_SHIFT_MASK, - modifiers & OSX_CAPITAL_MASK); - if (rime_keycode) { - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) - if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || - keyCode == 0x47 || keyCode == 0x40) { + int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], + modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); + int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) + if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || + keyCode == 0x47 || keyCode == 0x40) { rime_modifiers ^= kHyperMask; - } - handled = [self processKey:rime_keycode modifiers:rime_modifiers]; - [self rimeUpdate]; } + handled = [self processKey:rime_keycode modifiers:rime_modifiers]; + [self rimeUpdate]; } break; - case NSEventTypeLeftMouseDown: { - [self commitComposition:_currentClient]; - } break; +// case NSEventTypeLeftMouseDown: { +// if (_preeditString.length) { +// [_currentClient makeFirstResponder:nil]; +// [self commitComposition:_currentClient]; +// } +// } break; default: break; } @@ -218,9 +218,11 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (handled) { bool is_chording_key = (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || + (rime_keycode >= XK_nobreakspace && rime_keycode <= XK_ydiaeresis) || rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R || + rime_keycode == XK_Hyper_L; if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; @@ -297,7 +299,7 @@ -(void)clearChord -(NSUInteger)recognizedEvents:(id)sender { //NSLog(@"recognizedEvents:"); - return NSEventMaskKeyDown | NSEventMaskFlagsChanged | NSEventMaskLeftMouseDown; + return NSEventMaskKeyDown | NSEventMaskFlagsChanged; } -(void)activateServer:(id)sender @@ -348,13 +350,14 @@ -(void)deactivateServer:(id)sender -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); - // commit raw input - if (_session) { - const char* raw_input = rime_get_api()->get_input(_session); - if (raw_input) { - [self commitString: @(raw_input)]; - rime_get_api()->clear_composition(_session); + if (_session && _preeditString.length > 0) { + if ([_preeditString isEqualToString:@" "]) { + const char* raw_input = rime_get_api()->get_input(_session); + [self commitString:@(raw_input)]; + } else { // inline mode + [self commitString:_preeditString]; } + rime_get_api()->clear_composition(_session); } } @@ -577,10 +580,11 @@ -(void)rimeUpdate [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; } else { NSRange empty = {0, 0}; - // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. - // note this is a full-shape space U+3000; using half shape characters like "..." will result in - // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; +// // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. +// // note this is a full-shape space U+3000; using half shape characters like "..." will result in +// // an unstable baseline when composing Chinese characters. +// [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; + [self showPreeditString:(preedit ? @"\x02" : @"") selRange:empty caretPos:0]; } } // update candidates diff --git a/librime b/librime index 7e7f341a8..01bc6d021 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 7e7f341a817c5a077299b0adeced3bab01f86377 +Subproject commit 01bc6d0210c0c465ee606675b16a2a2919c45440 diff --git a/macos_keycode.h b/macos_keycode.h index 05b85f990..db17ceaac 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -3,13 +3,20 @@ #define _MACOS_KEYCODE_H_ // masks - -#define OSX_CAPITAL_MASK 1 << 16 -#define OSX_SHIFT_MASK 1 << 17 -#define OSX_CTRL_MASK 1 << 18 -#define OSX_ALT_MASK 1 << 19 -#define OSX_COMMAND_MASK 1 << 20 -#define OSX_FN_MASK 1 << 23 +#define OSX_LEFT_CTRL_MASK 1 << 0 +#define OSX_LEFT_SHIFT_MASK 1 << 1 +#define OSX_RIGHT_SHIFT_MASK 1 << 2 +#define OSX_LEFT_COMMAND_MASK 1 << 3 +#define OSX_RIGHT_COMMAND_MASK 1 << 4 +#define OSX_LEFT_ALT_MASK 1 << 5 +#define OSX_RIGHT_ALT_MASK 1 << 6 +#define OSX_RIGHT_CTRL_MASK 1 << 13 +#define OSX_CAPITAL_MASK 1 << 16 +#define OSX_SHIFT_MASK 1 << 17 +#define OSX_CTRL_MASK 1 << 18 +#define OSX_ALT_MASK 1 << 19 +#define OSX_COMMAND_MASK 1 << 20 +#define OSX_FN_MASK 1 << 23 // key codes // diff --git a/macos_keycode.m b/macos_keycode.m index a4437a324..7cbc2a070 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -128,21 +128,22 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) return keychar - 'a' + 'A'; } - if (keychar >= 0x20 && keychar <= 0x7e) { + if ((keychar >= 0x0020 && keychar <= 0x007e) || + (keychar >= 0x00a0 && keychar <= 0x00ff)) { return keychar; } - else if (keychar == 0x1b) { // ^[ - return XK_bracketleft; - } - else if (keychar == 0x1c) { // ^\ - return XK_backslash; - } - else if (keychar == 0x1d) { // ^] - return XK_bracketright; - } - else if (keychar == 0x1f) { // ^_ - return XK_minus; - } +// else if (keychar == 0x1b) { // ^[ +// return XK_bracketleft; +// } +// else if (keychar == 0x1c) { // ^\ +// return XK_backslash; +// } +// else if (keychar == 0x1d) { // ^] +// return XK_bracketright; +// } +// else if (keychar == 0x1f) { // ^_ +// return XK_minus; +// } return XK_VoidSymbol; } From a56884b0b14c6b653cf4d1d13137a9a04dee05d0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 21 Mar 2023 18:27:03 +0100 Subject: [PATCH 034/164] Panel improvement - label numbering: replace 10 with 0 - label font face: default to system monospaced-digit - writing direction: more robust bidi support --- SquirrelPanel.m | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 1fa43309c..7b9a7532a 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -849,7 +849,11 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { highlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; + labelAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize + weight:NSFontWeightRegular]; NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; + labelHighlightedAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize + weight:NSFontWeightRegular]; NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; commentAttrs[NSForegroundColorAttributeName] = secondaryTextColor; @@ -1146,7 +1150,7 @@ - (void)showPreedit:(NSString *)preedit } else { // default: 1. 2. 3... NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, i+1]; + labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; } [line appendAttributedString: @@ -1167,8 +1171,7 @@ - (void)showPreedit:(NSString *)preedit [line appendAttributedString:[[NSAttributedString alloc] initWithString:candidate.precomposedStringWithCanonicalMapping attributes:attrs]]; - // Use left-to-right marks to prevent right-to-left text from changing the - // layout of non-candidate text. + // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; if (theme.vertical) { convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); @@ -1186,7 +1189,7 @@ - (void)showPreedit:(NSString *)preedit } else { // default: 1. 2. 3... NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, i+1]; + labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; } NSUInteger suffixLabelStart = line.length; [line appendAttributedString: @@ -1232,6 +1235,9 @@ - (void)showPreedit:(NSString *)preedit if (theme.vertical) { paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); } + // Use left-to-right marks to declare the default writing direction and + // prevent strong right-to-left characters from setting the wirint direction + paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate @@ -1588,11 +1594,12 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } } if (labelFont == nil) { - if (fontDescriptor != nil) { - labelFont = [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize]; - } else { - labelFont = [NSFont fontWithName:font.fontName size:labelFontSize]; - } +// if (fontDescriptor != nil) { +// labelFont = [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize]; +// } else { +// labelFont = [NSFont fontWithName:font.fontName size:labelFontSize]; +// } + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; From 511352bb521fb56f21187b0b82a18e68733effc1 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 23 Mar 2023 04:10:04 +0100 Subject: [PATCH 035/164] Misc fix --- Base.lproj/MainMenu.xib | 2 +- Info.plist | 16 +++++++-------- SquirrelInputController.m | 36 ++++++++++++++++----------------- input_source.m | 20 +++++++++--------- librime | 2 +- macos_keycode.m | 27 ++++++++++++------------- main.m | 6 +++--- zh-HK.lproj/InfoPlist.strings | 8 ++++---- zh-HK.lproj/MainMenu.xib | 2 +- zh-Hans.lproj/InfoPlist.strings | 2 +- zh-Hans.lproj/MainMenu.xib | 2 +- zh-Hant.lproj/InfoPlist.strings | 2 +- zh-Hant.lproj/MainMenu.xib | 2 +- 13 files changed, 64 insertions(+), 63 deletions(-) diff --git a/Base.lproj/MainMenu.xib b/Base.lproj/MainMenu.xib index 29871b6cd..8b1464468 100644 --- a/Base.lproj/MainMenu.xib +++ b/Base.lproj/MainMenu.xib @@ -21,7 +21,7 @@ - + diff --git a/Info.plist b/Info.plist index 402d73ea1..26dd4c4e1 100644 --- a/Info.plist +++ b/Info.plist @@ -24,12 +24,12 @@ tsInputModeListKey - im.rime.inputmethod.Squirrel.Cant + im.rime.inputmethod.Squirrel.Hant TISInputSourceID - im.rime.inputmethod.Squirrel.Cant + im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage - yue-Hant + zh-Hant tsInputModeAlternateMenuIconFileKey rime.pdf tsInputModeCharacterRepertoireKey @@ -38,7 +38,7 @@ Hans tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -66,7 +66,7 @@ Hant tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -80,12 +80,12 @@ tsInputModeScriptKey smUnicodeScript - im.rime.inputmethod.Squirrel.Hant + im.rime.inputmethod.Squirrel.Cant TISInputSourceID - im.rime.inputmethod.Squirrel.Hant + im.rime.inputmethod.Squirrel.Cant TISIntendedLanguage - zh-Hant + yue-Hant tsInputModeAlternateMenuIconFileKey rime.pdf tsInputModeCharacterRepertoireKey diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 640a1a028..657291a24 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -146,23 +146,25 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender int keyCode = event.keyCode; NSString* keyChars = event.charactersIgnoringModifiers; - if ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) { + if (!isalpha(keyChars.UTF8String[0])) { keyChars = event.characters; } //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents - int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], - modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) - if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || - keyCode == 0x47 || keyCode == 0x40) { + int rime_keycode = osx_keycode_to_rime_keycode(keyCode, keyChars.UTF8String[0], + modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); + if (rime_keycode) { + int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) + if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || + keyCode == 0x47 || keyCode == 0x40) { rime_modifiers ^= kHyperMask; + } + handled = [self processKey:rime_keycode modifiers:rime_modifiers]; + [self rimeUpdate]; } - handled = [self processKey:rime_keycode modifiers:rime_modifiers]; - [self rimeUpdate]; } break; // case NSEventTypeLeftMouseDown: { // if (_preeditString.length) { @@ -218,11 +220,9 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (handled) { bool is_chording_key = (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - (rime_keycode >= XK_nobreakspace && rime_keycode <= XK_ydiaeresis) || rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R || - rime_keycode == XK_Hyper_L; + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; @@ -353,7 +353,8 @@ -(void)commitComposition:(id)sender if (_session && _preeditString.length > 0) { if ([_preeditString isEqualToString:@" "]) { const char* raw_input = rime_get_api()->get_input(_session); - [self commitString:@(raw_input)]; + if (raw_input) + [self commitString:@(raw_input)]; } else { // inline mode [self commitString:_preeditString]; } @@ -580,11 +581,10 @@ -(void)rimeUpdate [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; } else { NSRange empty = {0, 0}; -// // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. -// // note this is a full-shape space U+3000; using half shape characters like "..." will result in -// // an unstable baseline when composing Chinese characters. -// [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; - [self showPreeditString:(preedit ? @"\x02" : @"") selRange:empty caretPos:0]; + // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. + // note this is a full-shape space U+3000; using half shape characters like "..." will result in + // an unstable baseline when composing Chinese characters. + [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; } } // update candidates diff --git a/input_source.m b/input_source.m index deebd080c..e649e5f62 100644 --- a/input_source.m +++ b/input_source.m @@ -14,7 +14,7 @@ #define HANT_INPUT_MODE (1 << 1) #define CANT_INPUT_MODE (1 << 2) -void RegisterInputSource() { +void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); if (installedLocationURL) { @@ -32,12 +32,12 @@ void ActivateInputSource(int enabled_modes) { NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceID)); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kHansInputModeID] & - ((enabled_modes & HANS_INPUT_MODE) != 0) || - [sourceID isEqualToString:kHantInputModeID] & - ((enabled_modes & HANT_INPUT_MODE) != 0) || - [sourceID isEqualToString:kCantInputModeID] & - ((enabled_modes & CANT_INPUT_MODE) != 0)) { + if (([sourceID isEqualToString:kHansInputModeID] && + ((enabled_modes & HANS_INPUT_MODE) != 0)) || + ([sourceID isEqualToString:kHantInputModeID] && + ((enabled_modes & HANT_INPUT_MODE) != 0)) || + ([sourceID isEqualToString:kCantInputModeID] && + ((enabled_modes & CANT_INPUT_MODE) != 0))) { TISEnableInputSource(inputSource); NSLog(@"Enabled input source: %@", sourceID); CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( @@ -51,7 +51,7 @@ void ActivateInputSource(int enabled_modes) { CFRelease(sourceList); } -void DeactivateInputSource() { +void DeactivateInputSource(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( @@ -73,7 +73,7 @@ void DeactivateInputSource() { CFRelease(sourceList); } -int GetEnabledInputModes() { +int GetEnabledInputModes(void) { int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { @@ -92,6 +92,8 @@ int GetEnabledInputModes() { input_modes |= HANS_INPUT_MODE; else if ([sourceID isEqualToString:kHantInputModeID]) input_modes |= HANT_INPUT_MODE; + else if ([sourceID isEqualToString:kCantInputModeID]) + input_modes |= CANT_INPUT_MODE; } } } diff --git a/librime b/librime index 01bc6d021..020724e45 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 01bc6d0210c0c465ee606675b16a2a2919c45440 +Subproject commit 020724e45ccaf7756fa1f8104399d614efdb068f diff --git a/macos_keycode.m b/macos_keycode.m index 7cbc2a070..a4437a324 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -128,22 +128,21 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) return keychar - 'a' + 'A'; } - if ((keychar >= 0x0020 && keychar <= 0x007e) || - (keychar >= 0x00a0 && keychar <= 0x00ff)) { + if (keychar >= 0x20 && keychar <= 0x7e) { return keychar; } -// else if (keychar == 0x1b) { // ^[ -// return XK_bracketleft; -// } -// else if (keychar == 0x1c) { // ^\ -// return XK_backslash; -// } -// else if (keychar == 0x1d) { // ^] -// return XK_bracketright; -// } -// else if (keychar == 0x1f) { // ^_ -// return XK_minus; -// } + else if (keychar == 0x1b) { // ^[ + return XK_bracketleft; + } + else if (keychar == 0x1c) { // ^\ + return XK_backslash; + } + else if (keychar == 0x1d) { // ^] + return XK_bracketright; + } + else if (keychar == 0x1f) { // ^_ + return XK_minus; + } return XK_VoidSymbol; } diff --git a/main.m b/main.m index f4f95d6f7..f71129ce6 100644 --- a/main.m +++ b/main.m @@ -5,9 +5,9 @@ #import #import -void RegisterInputSource(); -int GetEnabledInputModes(); -void DeactivateInputSource(); +void RegisterInputSource(void); +int GetEnabledInputModes(void); +void DeactivateInputSource(void); void ActivateInputSource(int input_modes); #define DEFAULT_INPUT_MODE 1 diff --git a/zh-HK.lproj/InfoPlist.strings b/zh-HK.lproj/InfoPlist.strings index 4e8f3bef3..2a7e36b3e 100644 --- a/zh-HK.lproj/InfoPlist.strings +++ b/zh-HK.lproj/InfoPlist.strings @@ -2,10 +2,10 @@ NSHumanReadableCopyright = "式恕堂🄯版權所無"; -im.rime.inputmethod.Squirrel = "鼠鬚管"; +im.rime.inputmethod.Squirrel = "鼠鬚筆"; im.rime.inputmethod.Squirrel.Hans = "鼠须管"; im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; -im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚筆"; -CFBundleName = "鼠鬚管"; -CFBundleDisplayName = "鼠鬚管"; +CFBundleName = "鼠鬚筆"; +CFBundleDisplayName = "鼠鬚筆"; diff --git a/zh-HK.lproj/MainMenu.xib b/zh-HK.lproj/MainMenu.xib index 25bbc320a..c37d9596f 100644 --- a/zh-HK.lproj/MainMenu.xib +++ b/zh-HK.lproj/MainMenu.xib @@ -21,7 +21,7 @@ - + diff --git a/zh-Hans.lproj/InfoPlist.strings b/zh-Hans.lproj/InfoPlist.strings index 4a06de222..675a0ec7c 100644 --- a/zh-Hans.lproj/InfoPlist.strings +++ b/zh-Hans.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ NSHumanReadableCopyright = "式恕堂🄯版权所无"; im.rime.inputmethod.Squirrel = "鼠须管"; im.rime.inputmethod.Squirrel.Hans = "鼠须管"; im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; -im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚筆"; CFBundleName = "鼠须管"; CFBundleDisplayName = "鼠须管"; diff --git a/zh-Hans.lproj/MainMenu.xib b/zh-Hans.lproj/MainMenu.xib index 3e9cb3c9e..cca9280d6 100644 --- a/zh-Hans.lproj/MainMenu.xib +++ b/zh-Hans.lproj/MainMenu.xib @@ -21,7 +21,7 @@ - + diff --git a/zh-Hant.lproj/InfoPlist.strings b/zh-Hant.lproj/InfoPlist.strings index 4e8f3bef3..ba8d5048a 100644 --- a/zh-Hant.lproj/InfoPlist.strings +++ b/zh-Hant.lproj/InfoPlist.strings @@ -5,7 +5,7 @@ NSHumanReadableCopyright = "式恕堂🄯版權所無"; im.rime.inputmethod.Squirrel = "鼠鬚管"; im.rime.inputmethod.Squirrel.Hans = "鼠须管"; im.rime.inputmethod.Squirrel.Hant = "鼠鬚管"; -im.rime.inputmethod.Squirrel.Cant = "鼠鬚管"; +im.rime.inputmethod.Squirrel.Cant = "鼠鬚筆"; CFBundleName = "鼠鬚管"; CFBundleDisplayName = "鼠鬚管"; diff --git a/zh-Hant.lproj/MainMenu.xib b/zh-Hant.lproj/MainMenu.xib index e90fa9a5d..532085c75 100644 --- a/zh-Hant.lproj/MainMenu.xib +++ b/zh-Hant.lproj/MainMenu.xib @@ -21,7 +21,7 @@ - + From 4c3e281981143f3adb7caefeb732b283e80f6533 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 24 Mar 2023 03:02:08 +0100 Subject: [PATCH 036/164] UI --- Info.plist | 6 +++--- SquirrelPanel.m | 56 ++++++++++++++++++++++++++++++------------------- input_source.m | 6 +++--- main.m | 6 +++--- 4 files changed, 43 insertions(+), 31 deletions(-) diff --git a/Info.plist b/Info.plist index 26dd4c4e1..f8a9567f1 100644 --- a/Info.plist +++ b/Info.plist @@ -38,7 +38,7 @@ Hans tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -66,7 +66,7 @@ Hant tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey @@ -94,7 +94,7 @@ Hans tsInputModeDefaultStateKey - + tsInputModeIsVisibleKey tsInputModeKeyEquivalentModifiersKey diff --git a/SquirrelPanel.m b/SquirrelPanel.m index f96e79b6b..1fd164f54 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -242,6 +242,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _textView.selectable = NO; [_textView replaceTextContainer:textContainer]; _textView.layoutManager.backgroundLayoutEnabled = YES; + // _textView.layoutManager.usesFontLeading = NO; _defaultTheme = [[SquirrelTheme alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; @@ -399,7 +400,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe bodyRect->origin.y += leadingRect->size.height; } } - // Multiline, has trainling characters + // Multiline, has trailing characters if (firstLineRange.location < lastLineRange.location && NSMaxRange(lastLineRange) > NSMaxRange(glyphRange)) { *trailingRect = [layoutManager boundingRectForGlyphRange: NSMakeRange(lastLineRange.location, NSMaxRange(glyphRange)-lastLineRange.location) @@ -793,9 +794,11 @@ void fixDefaultFont(NSMutableAttributedString *text) { long i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; + NSNumber *baselineOffset = @(charFont.pointSize * -0.1); if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; + [text addAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:currentFontRange]; } i = currentFontRange.location + currentFontRange.length; } @@ -988,25 +991,22 @@ - (void)show { NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } - if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { - windowRect.origin.x = NSMaxX(_screenRect) - NSWidth(windowRect); + if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { // can only happen with horizontal + windowRect.origin.x = NSMinX(_position) - NSWidth(windowRect) - kOffsetHeight; } - if (NSMinX(windowRect) < NSMinX(_screenRect)) { - windowRect.origin.x = NSMinX(_screenRect); + if (NSMinX(windowRect) < NSMinX(_screenRect)) { // can only happen with vertical + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - if (theme.vertical) { + if (theme.vertical) { // failed attempt: NSMaxY(_position) + kOffsetHeight; windowRect.origin.y = NSMinY(_screenRect); } else { windowRect.origin.y = NSMaxY(_position) + kOffsetHeight; } } - if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { + if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { // failed attempt: NSMaxY(_position) + kOffsetHeight; windowRect.origin.y = NSMaxY(_screenRect) - NSHeight(windowRect); } - if (NSMinY(windowRect) < NSMinY(_screenRect)) { - windowRect.origin.y = NSMinY(_screenRect); - } [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { @@ -1181,9 +1181,11 @@ - (void)showPreedit:(NSString *)preedit [line appendAttributedString:candidateAttributedString]; - // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; - + // Use left-to-right embedding to prevent right-to-left text from changing the + // layout of non-candidate text. + [line addAttribute:NSWritingDirectionAttributeName value:@[@0] + range:NSMakeRange(candidateStart, line.length - candidateStart)]; + if (theme.suffixLabelFormat != nil) { NSString *labelString; if (labels.count > 1 && i < labels.count) { @@ -1241,18 +1243,19 @@ - (void)showPreedit:(NSString *)preedit NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (i == 0) { - paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; +// paragraphStyleCandidate.lineSpacing = theme.linespace + theme.hilitedCornerRadius; + paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace + theme.hilitedCornerRadius; } else { [text appendAttributedString:separator]; } - if (theme.linear) { - paragraphStyleCandidate.lineSpacing = theme.linespace; + if (!theme.linear) { +// paragraphStyleCandidate.lineSpacing = theme.linespace; + paragraphStyleCandidate.headIndent = labelWidth; } // Use left-to-right marks to declare the default writing direction and // prevent strong right-to-left characters from setting the wirint direction paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; - paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; @@ -1415,7 +1418,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; - CGFloat shadowSize = fmax(0,[config getDouble:@"style/shadow_size"]); + CGFloat shadowSize = fmax(0, [config getDouble:@"style/shadow_size"]); NSColor *backgroundColor; NSColor *borderColor; @@ -1655,14 +1658,23 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } } + CGFloat lineHeight = fmax(fmax(fontSize, labelFontSize), commentFontSize) * 1.15; + NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.paragraphSpacing = lineSpacing / 2; - paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing; + paragraphStyle.maximumLineHeight = lineHeight + lineSpacing; +// paragraphStyle.paragraphSpacing = lineSpacing / 2; +// paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + paragraphStyle.lineSpacing = lineSpacing; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - preeditParagraphStyle.paragraphSpacing = spacing / 2 + hilitedCornerRadius / 2; + preeditParagraphStyle.minimumLineHeight = fontSize * 1.15 + spacing; + preeditParagraphStyle.maximumLineHeight = fontSize * 1.15 + spacing; +// preeditParagraphStyle.paragraphSpacing = spacing + hilitedCornerRadius - lineSpacing; + preeditParagraphStyle.lineSpacing = spacing; + preeditParagraphStyle.paragraphSpacing = hilitedCornerRadius; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -1741,7 +1753,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius - shadowSize:shadowSize + shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) linespace:lineSpacing diff --git a/input_source.m b/input_source.m index e649e5f62..2f8cedd12 100644 --- a/input_source.m +++ b/input_source.m @@ -14,7 +14,7 @@ #define HANT_INPUT_MODE (1 << 1) #define CANT_INPUT_MODE (1 << 2) -void RegisterInputSource(void) { +void RegisterInputSource() { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); if (installedLocationURL) { @@ -51,7 +51,7 @@ void ActivateInputSource(int enabled_modes) { CFRelease(sourceList); } -void DeactivateInputSource(void) { +void DeactivateInputSource() { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( @@ -73,7 +73,7 @@ void DeactivateInputSource(void) { CFRelease(sourceList); } -int GetEnabledInputModes(void) { +int GetEnabledInputModes() { int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { diff --git a/main.m b/main.m index f71129ce6..f4f95d6f7 100644 --- a/main.m +++ b/main.m @@ -5,9 +5,9 @@ #import #import -void RegisterInputSource(void); -int GetEnabledInputModes(void); -void DeactivateInputSource(void); +void RegisterInputSource(); +int GetEnabledInputModes(); +void DeactivateInputSource(); void ActivateInputSource(int input_modes); #define DEFAULT_INPUT_MODE 1 From ec437a27063f1f73ad837f3021827b95e06e6e15 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 24 Mar 2023 07:24:00 +0100 Subject: [PATCH 037/164] Revert pr/700 --- SquirrelPanel.m | 927 +++++++++++++++++++++++------------------------- librime | 2 +- utf8.cpp | 5 +- 3 files changed, 446 insertions(+), 488 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 1fd164f54..51506ba3a 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,16 +3,61 @@ #import "SquirrelConfig.h" #import +@implementation NSBezierPath (BezierPathQuartzUtilities) +// This method works only in OS X v10.2 and later. +- (CGPathRef)quartzPath { + NSInteger i, numElements; + // Need to begin a path here. + CGPathRef immutablePath = NULL; + + // Then draw the path elements. + numElements = [self elementCount]; + if (numElements > 0) { + CGMutablePathRef path = CGPathCreateMutable(); + NSPoint points[3]; + BOOL didClosePath = YES; + for (i = 0; i < numElements; i++) { + switch ([self elementAtIndex:i associatedPoints:points]) { + case NSMoveToBezierPathElement: + CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); + break; + case NSLineToBezierPathElement: + CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); + didClosePath = NO; + break; + case NSCurveToBezierPathElement: + CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, + points[1].x, points[1].y, + points[2].x, points[2].y); + didClosePath = NO; + break; + case NSClosePathBezierPathElement: + CGPathCloseSubpath(path); + didClosePath = YES; + break; + } + } + + // Be sure the path is closed or Quartz may not do valid hit detection. + if (!didClosePath) { + CGPathCloseSubpath(path); + } + immutablePath = CGPathCreateCopy(path); + CGPathRelease(path); + } + return immutablePath; +} +@end + static const CGFloat kOffsetHeight = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c.\u00A0%@"; +static NSString *const kDefaultCandidateFormat = @"%c. %@"; @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL native; -@property(nonatomic, assign) BOOL memorizeSize; @property(nonatomic, strong, readonly) NSColor *backgroundColor; @property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @@ -22,14 +67,12 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; -@property(nonatomic, readonly) CGFloat shadowSize; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; -@property(nonatomic, readonly) BOOL mutualExclusive; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -58,14 +101,12 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius - shadowSize:(CGFloat)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(BOOL)translucency - mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -127,27 +168,23 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(double)cornerRadius hilitedCornerRadius:(double)hilitedCornerRadius - shadowSize:(double)shadowSize edgeInset:(NSSize)edgeInset borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(double)alpha + alpha:(CGFloat)alpha translucency:(BOOL)translucency - mutualExclusive:(BOOL)mutualExclusive linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; - _shadowSize = shadowSize; _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; _alpha = alpha; _translucency = translucency; - _mutualExclusive = mutualExclusive; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -183,9 +220,8 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextView *textView; -@property(nonatomic, readonly) NSArray *candidateRanges; -@property(nonatomic, readonly) NSInteger hilightedIndex; +@property(nonatomic, readonly) NSTextStorage *text; +@property(nonatomic, readonly) NSRange highlightedRange; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRect contentRect; @@ -194,10 +230,12 @@ @interface SquirrelView : NSView @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; -- (void) drawViewWith:(NSArray *)candidateRanges - hilightedIndex:(NSInteger)hilightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; +- (BOOL)isFlipped; +@property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; +- (void)setText:(NSAttributedString *)text; +- (void)drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -234,61 +272,45 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } - _textView = [[NSTextView alloc] initWithFrame:frameRect]; + // Use textStorage to store text and manage all text layout and draws NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - [_textView replaceTextContainer:textContainer]; - _textView.layoutManager.backgroundLayoutEnabled = YES; - // _textView.layoutManager.usesFontLeading = NO; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + [layoutManager addTextContainer:textContainer]; + _text = [[NSTextStorage alloc] init]; + [_text addLayoutManager:layoutManager]; + layoutManager.backgroundLayoutEnabled = YES; _defaultTheme = [[SquirrelTheme alloc] init]; + _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } - _shape = [[CAShapeLayer alloc] init]; return self; } // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; - __block long actualWidth = 0; - [_textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { - NSRange range = [self.textView.layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; - NSAttributedString *str = [self.textView.textStorage attributedSubstringFromRange:range]; - NSRange nonWhiteRange = [str.string rangeOfCharacterFromSet:NSCharacterSet.whitespaceAndNewlineCharacterSet.invertedSet options:NSBackwardsSearch]; - if (nonWhiteRange.location != NSNotFound) { - NSRange newRange = NSMakeRange(range.location, NSMaxRange(nonWhiteRange)); - NSRange newGlyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:newRange actualCharacterRange:NULL]; - CGFloat width = [self.textView.layoutManager boundingRectForGlyphRange:newGlyphRange inTextContainer:self.textView.textContainer].size.width; - if (width > actualWidth) { - actualWidth = width; - } - } - }]; - if (actualWidth > 0) { - rect.size.width = actualWidth; - } + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; return rect; } +- (void)setText:(NSAttributedString *)text { + [_text setAttributedString:[text copy]]; +} + // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWith:(NSArray *)candidateRanges - hilightedIndex:(NSInteger)hilightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { - _candidateRanges = candidateRanges; - _hilightedIndex = hilightedIndex; +- (void)drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange { + _highlightedRange = hilightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; self.needsDisplay = YES; @@ -300,15 +322,14 @@ - (void) drawViewWith:(NSArray *)candidateRanges return 1; } else if (number <= -2) { return -1; - } else { + }else { return number / 2; } } // Bezier cubic curve, which has continuous roundness -CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * __nullable straightCorner, CGFloat alpha, CGFloat beta) { - beta = MAX(0.00001, beta); - CGMutablePathRef path = CGPathCreateMutable(); +NSBezierPath *drawSmoothLines(NSArray *vertex, CGFloat alpha, CGFloat beta) { + NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) return path; NSPoint previousPoint = (vertex[vertex.count-1]).pointValue; @@ -318,39 +339,40 @@ CGMutablePathRef drawSmoothLines(NSArray *vertex, NSSet * NSPoint control2; NSPoint target = previousPoint; NSPoint diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (!straightCorner || ![straightCorner containsObject:[NSNumber numberWithUnsignedInteger:vertex.count - 1]]) { + if (ABS(diff.x) >= ABS(diff.y)) { target.x += sign(diff.x/beta)*beta; + } else { target.y += sign(diff.y/beta)*beta; } - CGPathMoveToPoint(path, NULL, target.x, target.y); + [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; i += 1) { previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; point = (vertex[i]).pointValue; nextPoint = (vertex[(i+1)%vertex.count]).pointValue; target = point; - if (straightCorner && [straightCorner containsObject:[NSNumber numberWithUnsignedInteger:i]]) { - CGPathAddLineToPoint(path, NULL, target.x, target.y); - } else { - control1 = point; - diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); + control1 = point; + diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); + if (ABS(diff.x) >= ABS(diff.y)) { target.x -= sign(diff.x/beta)*beta; control1.x -= sign(diff.x/beta)*alpha; + } else { target.y -= sign(diff.y/beta)*beta; control1.y -= sign(diff.y/beta)*alpha; - - CGPathAddLineToPoint(path, NULL, target.x, target.y); - target = point; - control2 = point; - diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); + } + [path lineToPoint:target]; + target = point; + control2 = point; + diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); + if (ABS(diff.x) > ABS(diff.y)) { control2.x += sign(diff.x/beta)*alpha; target.x += sign(diff.x/beta)*beta; + } else { control2.y += sign(diff.y/beta)*alpha; target.y += sign(diff.y/beta)*beta; - - CGPathAddCurveToPoint(path, NULL, control1.x, control1.y, control2.x, control2.y, target.x, target.y); } + [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; } - CGPathCloseSubpath(path); + [path closePath]; return path; } @@ -378,55 +400,58 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle -- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect extraSurounding:(CGFloat)extraSurounding { - NSLayoutManager *layoutManager = _textView.layoutManager; - NSTextContainer *textContainer = _textView.textContainer; +- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { + NSLayoutManager *layoutManager = _text.layoutManagers[0]; + NSTextContainer *textContainer = layoutManager.textContainers[0]; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange firstLineRange = NSMakeRange(NSNotFound, 0); - [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&firstLineRange]; - NSRange lastLineRange = NSMakeRange(NSNotFound, 0); - [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&lastLineRange]; - + NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; *leadingRect = NSZeroRect; *bodyRect = boundingRect; *trailingRect = NSZeroRect; - - // Multiline, not starting from beginning - if (firstLineRange.location < lastLineRange.location && firstLineRange.location < glyphRange.location) { - *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(glyphRange.location, NSMaxRange(firstLineRange)-glyphRange.location) inTextContainer:textContainer]; + if (boundingRect.origin.x <= 1 && fullRangeInBoundingRect.location < glyphRange.location) { + *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(fullRangeInBoundingRect.location, glyphRange.location-fullRangeInBoundingRect.location) inTextContainer:textContainer]; if (!nearEmptyRect(*leadingRect)) { bodyRect->size.height -= leadingRect->size.height; bodyRect->origin.y += leadingRect->size.height; } + double rightEdge = NSMaxX(*leadingRect); + leadingRect->origin.x = rightEdge; + leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; } - // Multiline, has trailing characters - if (firstLineRange.location < lastLineRange.location && NSMaxRange(lastLineRange) > NSMaxRange(glyphRange)) { + if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(lastLineRange.location, NSMaxRange(glyphRange)-lastLineRange.location) + NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; } + double leftEdge = NSMinX(*trailingRect); + trailingRect->origin.x = bodyRect->origin.x; + trailingRect->size.width = leftEdge - bodyRect->origin.x; + } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { + *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; + if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { + *trailingRect = NSZeroRect; + } else if (!nearEmptyRect(*trailingRect)) { + bodyRect->size.height -= trailingRect->size.height; + } } - - if ((!nearEmptyRect(*leadingRect) && bodyRect->size.height < leadingRect->size.height/2) || (!nearEmptyRect(*trailingRect) && bodyRect->size.height < trailingRect->size.height/2)) { - *bodyRect = NSZeroRect; + NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; + lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; + NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; + NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { + lastLineRange.length -= 1; + glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; } - - if (extraSurounding > 0) { - if (nearEmptyRect(*leadingRect) && nearEmptyRect(*trailingRect)) { - expandHighlightWidth(bodyRect, extraSurounding); + if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { + if (!nearEmptyRect(*trailingRect)) { + *trailingRect = lastLineRect; } else { - if (!(nearEmptyRect(*leadingRect))) { - expandHighlightWidth(leadingRect, extraSurounding); - } - if (!(nearEmptyRect(*trailingRect))) { - expandHighlightWidth(trailingRect, extraSurounding); - } + *bodyRect = lastLineRect; } } - NSSize edgeInset = self.currentTheme.edgeInset; leadingRect->origin.x += edgeInset.width; leadingRect->origin.y += edgeInset.height; @@ -447,20 +472,20 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; + return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], bodyVertex[1]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { NSArray * trailingVertex = rectVertex(trailingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3], bodyVertex[0]]; + return @[bodyVertex[0], bodyVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1]]; + return @[trailingVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], leadingVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3], leadingVertex[0], leadingVertex[1], bodyVertex[0]]; + return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; } else { return @[]; } @@ -485,277 +510,239 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -CAShapeLayer *shapeFromPath(CGPathRef path) { - CAShapeLayer *layer = [CAShapeLayer layer]; - layer.path = path; - layer.fillRule = kCAFillRuleEvenOdd; - return layer; -} - // Add gap between horizontal candidates -void expandHighlightWidth(NSRect *rect, CGFloat extraSurrounding) { - if (!nearEmptyRect(*rect)) { - rect->size.width += extraSurrounding; - rect->origin.x -= extraSurrounding / 2; - } -} - -void removeCorner(NSMutableArray *highlightedPoints, NSMutableSet *rightCorners, NSRect containingRect) { - if (highlightedPoints && rightCorners) { - NSSet *originalRightCorners = [[NSSet alloc] initWithSet:rightCorners]; - for (NSNumber *cornerIndex in originalRightCorners) { - NSUInteger index = cornerIndex.unsignedIntegerValue; - NSPoint corner = [highlightedPoints[index] pointValue]; - CGFloat dist = MIN(NSMaxY(containingRect) - corner.y, corner.y - NSMinY(containingRect)); - if (dist < 1e-2) { - [rightCorners removeObject:cornerIndex]; - } - } - } -} - -- (void) linearMultilineForRect:(NSRect)bodyRect leadingRect:(NSRect)leadingRect trailingRect:(NSRect)trailingRect points1:(NSMutableArray **)highlightedPoints points2:(NSMutableArray **)highlightedPoints2 rightCorners:(NSMutableSet **)rightCorners rightCorners2:(NSMutableSet **)rightCorners2 { - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - *highlightedPoints = [rectVertex(leadingRect) mutableCopy]; - *highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(2), @(3), nil]; - } else { - *highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { - if (NSMaxX(trailingRect) < NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), @(4), @(5), nil]; - } else if (NSMaxX(trailingRect) >= NSMaxX(leadingRect) && NSMinX(trailingRect) < NSMinX(leadingRect)) { - *rightCorners = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; - } +- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { + if (_highlightedRange.location+_highlightedRange.length == _text.length) { + if (!nearEmptyRect(*rect)) { + rect->size.width += _seperatorWidth / 2; + rect->origin.x -= _seperatorWidth / 2; } - } - if ([*highlightedPoints2 count] > 0) { - *rightCorners2 = [[NSMutableSet alloc] initWithObjects:@(0), @(1), nil]; - } -} - -- (CGPathRef)drawHighlightedWith:(SquirrelTheme *)theme highlightedRange:(NSRange)highlightedRange backgroundRect:(NSRect)backgroundRect preeditRect:(NSRect)preeditRect containingRect:(NSRect)containingRect { - NSRect currentContainingRect = containingRect; - - CGFloat halfLinespace = theme.linespace / 2; - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; - } - innerBox.size.height -= halfLinespace; - NSRect outerBox = backgroundRect; - outerBox.size.height -= preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - outerBox.origin.y += preeditRect.size.height + MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - - double effectiveRadius = MAX(0, theme.hilitedCornerRadius); - CGMutablePathRef path = CGPathCreateMutable(); - - if (theme.linear){ - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect extraSurounding:_seperatorWidth]; - - NSMutableArray *highlightedPoints; - NSMutableArray *highlightedPoints2; - NSMutableSet *rightCorners; - NSMutableSet *rightCorners2; - [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPoints points2:&highlightedPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; - - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - // Expand the boxes to reach proper border - expand(highlightedPoints, innerBox, outerBox); - removeCorner(highlightedPoints, rightCorners, currentContainingRect); - - path = drawSmoothLines(highlightedPoints, rightCorners, 0.3*effectiveRadius, 1.4*effectiveRadius); - if (highlightedPoints2.count > 0) { - expand(highlightedPoints2, innerBox, outerBox); - removeCorner(highlightedPoints2, rightCorners2, currentContainingRect); - CGPathRef path2 = drawSmoothLines(highlightedPoints2, rightCorners2, 0.3*effectiveRadius, 1.4*effectiveRadius); - CGPathAddPath(path, NULL, path2); + } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + if (!nearEmptyRect(*rect)) { + rect->size.width += _seperatorWidth / 2; } } else { - NSRect highlightedRect = [self contentRectForRange:highlightedRange]; - highlightedRect.size.width = backgroundRect.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(backgroundRect.origin.x, highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (NSMaxRange(highlightedRange) == _textView.textStorage.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } + if (!nearEmptyRect(*rect)) { + rect->size.width += _seperatorWidth; + rect->origin.x -= _seperatorWidth / 2; } - NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - expand(highlightedPoints, innerBox, outerBox); - path = drawSmoothLines(highlightedPoints, nil, 0.3*effectiveRadius, 1.4*effectiveRadius); } - return path; } // All draws happen here - (void)drawRect:(NSRect)dirtyRect { - CGPathRef backgroundPath = CGPathCreateMutable(); - CGPathRef highlightedPath = CGPathCreateMutable(); - CGMutablePathRef highlightedPreeditPath = CGPathCreateMutable(); - CGPathRef preeditPath = CGPathCreateMutable(); + NSBezierPath *backgroundPath; + NSBezierPath *borderPath; + NSBezierPath *highlightedPath; + NSBezierPath *highlightedPath2; + NSBezierPath *highlightedPreeditPath; + NSBezierPath *highlightedPreeditPath2; + NSBezierPath *preeditPath; SquirrelTheme * theme = self.currentTheme; - NSPoint textFieldOrigin = dirtyRect.origin; - textFieldOrigin.y += theme.edgeInset.height; - textFieldOrigin.x += theme.edgeInset.width; + NSRect textField = dirtyRect; + textField.origin.y += theme.edgeInset.height; + textField.origin.x += theme.edgeInset.width; // Draw preedit Rect NSRect backgroundRect = dirtyRect; - NSRect containingRect = dirtyRect; - containingRect.size.height -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; - containingRect.size.width -= (theme.hilitedCornerRadius + theme.borderWidth) * 2; - containingRect.origin.x += theme.hilitedCornerRadius + theme.borderWidth; - containingRect.origin.y += theme.hilitedCornerRadius + theme.borderWidth; // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = backgroundRect.size.width; + preeditRect.size.width = textField.size.width; preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = backgroundRect.origin; - if (_candidateRanges.count == 0) { + preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); + if (_highlightedRange.length == 0) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; } if (theme.preeditBackgroundColor != nil) { - preeditPath = drawSmoothLines(rectVertex(preeditRect), nil, 0, 0); + preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); } } // Draw highlighted Rect - NSRange candidateRange = [_candidateRanges[_hilightedIndex] rangeValue]; - // Draw highlighted Rect - if (candidateRange.length > 0 && theme.highlightedStripColor != nil) { - highlightedPath = [self drawHighlightedWith:theme highlightedRange:candidateRange backgroundRect:backgroundRect preeditRect:preeditRect containingRect:containingRect]; + if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { + NSRect innerBox = backgroundRect; + innerBox.size.width -= (theme.edgeInset.width + 1) * 2; + innerBox.origin.x += theme.edgeInset.width + 1; + if (_preeditRange.length == 0) { + innerBox.origin.y += theme.edgeInset.height + 1; + innerBox.size.height -= (theme.edgeInset.height + 1) * 2; + } else { + innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; + innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + } + NSRect outerBox = backgroundRect; + outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; + outerBox.size.width -= theme.hilitedCornerRadius; + outerBox.origin.x += theme.hilitedCornerRadius / 2; + outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; + + CGFloat halfLinespace = theme.linespace / 2; + if (theme.linear){ + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + + [self addGapBetweenHorizontalCandidates:&leadingRect]; + [self addGapBetweenHorizontalCandidates:&bodyRect]; + [self addGapBetweenHorizontalCandidates:&trailingRect]; + + NSMutableArray *highlightedPoints; + NSMutableArray *highlightedPoints2; + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + highlightedPoints = [rectVertex(leadingRect) mutableCopy]; + highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; + } else { + highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + } + + xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); + xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); + innerBox.size.height -= halfLinespace; + // Expand the boxes to reach proper border + expand(highlightedPoints, innerBox, outerBox); + expand(highlightedPoints2, innerBox, outerBox); + highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + if (highlightedPoints2.count > 0) { + highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + } + } else { + NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; + highlightedRect.size.width = textField.size.width; + highlightedRect.size.height += theme.linespace; + highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, + highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); + if (_highlightedRange.location+_highlightedRange.length == _text.length) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + } + if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + if (_preeditRange.length == 0) { + highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + } else { + highlightedRect.size.height += theme.hilitedCornerRadius / 2; + highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + } + } + NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; + expand(highlightedPoints, innerBox, outerBox); + highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); + } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + NSRect innerBox = preeditRect; innerBox.size.width -= (theme.edgeInset.width + 1) * 2; innerBox.origin.x += theme.edgeInset.width + 1; innerBox.origin.y += theme.edgeInset.height + 1; - if (_candidateRanges.count == 0) { + if (_highlightedRange.length == 0) { innerBox.size.height -= (theme.edgeInset.height + 1) * 2; } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; } NSRect outerBox = preeditRect; - outerBox.size.height -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.size.width -= MAX(0, theme.hilitedCornerRadius + theme.borderWidth); - outerBox.origin.x += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - outerBox.origin.y += MAX(0, theme.hilitedCornerRadius + theme.borderWidth) / 2; - - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect extraSurounding:0]; - + outerBox.size.height -= theme.hilitedCornerRadius; + outerBox.size.width -= theme.hilitedCornerRadius; + outerBox.origin.x += theme.hilitedCornerRadius / 2; + outerBox.origin.y += theme.hilitedCornerRadius / 2; + NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; - NSMutableSet *rightCorners; - NSMutableSet *rightCorners2; - [self linearMultilineForRect:bodyRect leadingRect:leadingRect trailingRect:trailingRect points1:&highlightedPreeditPoints points2:&highlightedPreeditPoints2 rightCorners:&rightCorners rightCorners2:&rightCorners2]; - + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; + highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; + } else { + highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + } + // Expand the boxes to reach proper border expand(highlightedPreeditPoints, innerBox, outerBox); - removeCorner(highlightedPreeditPoints, rightCorners, containingRect); - highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, rightCorners, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + expand(highlightedPreeditPoints2, innerBox, outerBox); + highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - expand(highlightedPreeditPoints2, innerBox, outerBox); - removeCorner(highlightedPreeditPoints2, rightCorners2, containingRect); - CGPathRef highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, rightCorners2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - CGPathAddPath(highlightedPreeditPath, NULL, highlightedPreeditPath2); + highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } } [NSBezierPath setDefaultLineWidth:0]; - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), nil, theme.cornerRadius*0.3, theme.cornerRadius*1.4); - _shape.path = CGPathCreateMutableCopy(backgroundPath); - - [self.layer setSublayers: NULL]; - CGMutablePathRef backPath = CGPathCreateMutableCopy(backgroundPath); - if (!CGPathIsEmpty(preeditPath)) { - CGPathAddPath(backPath, NULL, preeditPath); - } - if (theme.mutualExclusive) { - if (!CGPathIsEmpty(highlightedPath)) { - CGPathAddPath(backPath, NULL, highlightedPath); - } - } - CAShapeLayer *panelLayer = shapeFromPath(backPath); - panelLayer.fillColor = theme.backgroundColor.CGColor; - CAShapeLayer *panelLayerMask = shapeFromPath(backgroundPath); - panelLayer.mask = panelLayerMask; - [self.layer addSublayer: panelLayer]; - - if (theme.preeditBackgroundColor && !CGPathIsEmpty(preeditPath)) { - CAShapeLayer *layer = shapeFromPath(preeditPath); - layer.fillColor = theme.preeditBackgroundColor.CGColor; - CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); - if (theme.mutualExclusive && !CGPathIsEmpty(highlightedPreeditPath)) { - CGPathAddPath(maskPath, NULL, highlightedPreeditPath); - } - CAShapeLayer *mask = shapeFromPath(maskPath); - layer.mask = mask; - [panelLayer addSublayer: layer]; - } - if (theme.borderWidth > 0 && theme.borderColor) { - CAShapeLayer *borderLayer = shapeFromPath(backgroundPath); - borderLayer.lineWidth = theme.borderWidth * 2; - borderLayer.strokeColor = theme.borderColor.CGColor; - borderLayer.fillColor = NULL; - [panelLayer addSublayer: borderLayer]; - } - if (theme.highlightedPreeditColor && !CGPathIsEmpty(highlightedPreeditPath)) { - CAShapeLayer *layer = shapeFromPath(highlightedPreeditPath); - layer.fillColor = theme.highlightedPreeditColor.CGColor; - [panelLayer addSublayer: layer]; - } - if (theme.highlightedStripColor && !CGPathIsEmpty(highlightedPath)) { - CAShapeLayer *layer = shapeFromPath(highlightedPath); - layer.fillColor = theme.highlightedStripColor.CGColor; - if (theme.shadowSize > 0) { - CAShapeLayer *shadowLayer = [CAShapeLayer layer]; - shadowLayer.shadowColor = NSColor.blackColor.CGColor; - shadowLayer.shadowOffset = NSMakeSize(theme.shadowSize/2, (theme.vertical ? -1 : 1) * theme.shadowSize/2); - shadowLayer.shadowPath = highlightedPath; - shadowLayer.shadowRadius = theme.shadowSize; - shadowLayer.shadowOpacity = 0.2; - CGMutablePathRef maskPath = CGPathCreateMutableCopy(backgroundPath); - CGPathAddPath(maskPath, NULL, highlightedPath); - if (!CGPathIsEmpty(preeditPath)) { - CGPathAddPath(maskPath, NULL, preeditPath); + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); + _shape.path = backgroundPath.quartzPath; + // Nothing should extend beyond backgroundPath + borderPath = [backgroundPath copy]; + [borderPath addClip]; + borderPath.lineWidth = theme.borderWidth; + +// This block of code enables independent transparencies in highlighted colour and background colour. +// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. +#if 0 + // Calculate intersections. + if (![highlightedPath isEmpty]) { + [backgroundPath appendBezierPath:[highlightedPath copy]]; + if (![highlightedPath2 isEmpty]) { + [backgroundPath appendBezierPath:[highlightedPath2 copy]]; + } + } + + if (![preeditPath isEmpty]) { + [backgroundPath appendBezierPath:[preeditPath copy]]; + } + + if (![highlightedPreeditPath isEmpty]) { + if (preeditPath != nil) { + [preeditPath appendBezierPath:[highlightedPreeditPath copy]]; + } else { + [backgroundPath appendBezierPath:[highlightedPreeditPath copy]]; + } + if (![highlightedPreeditPath2 isEmpty]) { + if (preeditPath != nil) { + [preeditPath appendBezierPath:[highlightedPreeditPath2 copy]]; + } else { + [backgroundPath appendBezierPath:[highlightedPreeditPath2 copy]]; } - CAShapeLayer *shadowLayerMask = shapeFromPath(maskPath); - shadowLayer.mask = shadowLayerMask; - layer.strokeColor = [NSColor.blackColor colorWithAlphaComponent:0.15].CGColor; - layer.lineWidth = 0.5; - [layer addSublayer: shadowLayer]; } - [panelLayer addSublayer: layer]; } - [_textView setTextContainerInset:NSMakeSize(textFieldOrigin.x, textFieldOrigin.y)]; + [backgroundPath setWindingRule:NSEvenOddWindingRule]; + [preeditPath setWindingRule:NSEvenOddWindingRule]; +#endif + + [theme.backgroundColor setFill]; + [backgroundPath fill]; + if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { + [theme.preeditBackgroundColor setFill]; + [preeditPath fill]; + } + if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { + [theme.highlightedStripColor setFill]; + [highlightedPath fill]; + if (![highlightedPath2 isEmpty]) { + [highlightedPath2 fill]; + } + } + if (theme.highlightedPreeditColor && ![highlightedPreeditPath isEmpty]) { + [theme.highlightedPreeditColor setFill]; + [highlightedPreeditPath fill]; + if (![highlightedPreeditPath2 isEmpty]) { + [highlightedPreeditPath2 fill]; + } + } + + if (theme.borderColor && (theme.borderWidth > 0)) { + [theme.borderColor setStroke]; + [borderPath stroke]; + } + NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; + [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; } @end @@ -788,6 +775,42 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } +CGFloat minimumHeight(NSDictionary *attribute) { + const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; + const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; + return minimumHeight; +} + +// Use this method to convert charcters to upright position +// Based on the width of the chacter, relative font size matters +void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { + NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; + double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; + // Use the width of the character to determin if they should be upright in vertical writing mode. + // Adjust font base line for better alignment. + const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; + const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; + const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; + const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; + stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; + NSUInteger i = stringRange.location; + while (i < stringRange.location+stringRange.length) { + NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; + i = range.location + range.length; + NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + // Also adjust the baseline so upright and lying charcters are properly aligned + if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { + [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; + NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + CGFloat widthDiff = charRect.size.width-cjkChar.size.width; + CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; + [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; + } else { + [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; + } + } +} + void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -804,20 +827,6 @@ void fixDefaultFont(NSMutableAttributedString *text) { } } -NSAttributedString *insert(NSString *separator, NSAttributedString *betweenText) { - NSRange range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:0]; - NSAttributedString *attributedSeperator = [[NSAttributedString alloc] initWithString:separator attributes:[betweenText attributesAtIndex:0 effectiveRange:nil]]; - NSUInteger i = NSMaxRange(range); - NSMutableAttributedString *workingString = [[betweenText attributedSubstringFromRange:range] mutableCopy]; - while (i < betweenText.length) { - range = [betweenText.string rangeOfComposedCharacterSequenceAtIndex:i]; - [workingString appendAttributedString:attributedSeperator]; - [workingString appendAttributedString:[betweenText attributedSubstringFromRange:range]]; - i = NSMaxRange(range); - } - return workingString; -} - + (NSColor *)secondaryTextColor { if(@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -829,7 +838,6 @@ + (NSColor *)secondaryTextColor { - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; theme.native = YES; - theme.memorizeSize = YES; theme.candidateFormat = kDefaultCandidateFormat; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -903,8 +911,7 @@ - (instancetype)init { [contentView addSubview:_back]; } [contentView addSubview:_view]; - [contentView addSubview:_view.textView]; - + self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; if (@available(macOS 10.14, *)) { @@ -930,15 +937,6 @@ - (void)getCurrentScreen { } } -- (CGFloat)getMaxTextWidth:(SquirrelTheme *)theme { - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - return theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; -} - // Get the window size, the windows will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; @@ -952,20 +950,28 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = [self getMaxTextWidth:theme]; - CGFloat maxTextHeight = theme.vertical ? _screenRect.size.width - theme.edgeInset.width * 2 : _screenRect.size.height - theme.edgeInset.height * 2; - _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); + CGFloat textWidth = _view.text.size.width; + NSFont *currentFont = theme.attrs[NSFontAttributeName]; + CGFloat fontScale = currentFont.pointSize / 12; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + CGFloat maxTextWidth = theme.vertical + ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; + if (textWidth > maxTextWidth) { + textWidth = maxTextWidth; + } + _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if (theme.memorizeSize && ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || - (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect)))) { + if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); + _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); } } @@ -1010,17 +1016,14 @@ - (void)show { [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { - self.contentView.boundsRotation = -90; - _view.textView.boundsRotation = 0; + self.contentView.boundsRotation = -90.0; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; } else { self.contentView.boundsRotation = 0; - _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; } BOOL translucency = theme.translucency; [_view setFrame:self.contentView.bounds]; - [_view.textView setFrame:self.contentView.bounds]; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; @@ -1071,8 +1074,6 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; - [self getCurrentScreen]; - CGFloat maxTextWidth = [self getMaxTextWidth:theme]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; @@ -1105,8 +1106,13 @@ - (void)showPreedit:(NSString *)preedit } [text appendAttributedString:line]; + NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; + if (theme.vertical) { + convertToVerticalGlyph(text, NSMakeRange(0, line.length)); + paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); + } [text addAttribute:NSParagraphStyleAttributeName - value:theme.preeditParagraphStyle + value:paragraphStylePreedit range:NSMakeRange(0, text.length)]; _preeditRange = NSMakeRange(0, text.length); @@ -1118,25 +1124,17 @@ - (void)showPreedit:(NSString *)preedit candidateStartPos = text.length; } - NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; + NSRange highlightedRange = NSMakeRange(NSNotFound, 0); // candidates NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs; - NSDictionary *labelAttrs; - NSDictionary *commentAttrs; - if (i == index) { - attrs = theme.highlightedAttrs; - labelAttrs = theme.labelHighlightedAttrs; - commentAttrs = theme.commentHighlightedAttrs; - } else { - attrs = theme.attrs; - labelAttrs = theme.labelAttrs; - commentAttrs = theme.commentAttrs; - } - + NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; + NSDictionary *labelAttrs = + (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; + NSDictionary *commentAttrs = + (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { @@ -1159,33 +1157,25 @@ - (void)showPreedit:(NSString *)preedit initWithString:labelString attributes:labelAttrs]]; // get the label size for indent + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(0, line.length)); + } if (!theme.linear) { - NSMutableAttributedString *str = [line mutableCopy]; - if (theme.vertical) { - [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; - } - labelWidth = [str boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - NSAttributedString *candidateAttributedString = [[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]; - CGFloat candidateWidth = [candidateAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; - if (candidateWidth <= maxTextWidth * 0.2) { - // Unicode Word Joiner - candidateAttributedString = insert(@"\u2060", candidateAttributedString); - } - - [line appendAttributedString:candidateAttributedString]; - - // Use left-to-right embedding to prevent right-to-left text from changing the - // layout of non-candidate text. - [line addAttribute:NSWritingDirectionAttributeName value:@[@0] - range:NSMakeRange(candidateStart, line.length - candidateStart)]; - + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]]; + // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. + [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); + } + if (theme.suffixLabelFormat != nil) { NSString *labelString; if (labels.count > 1 && i < labels.count) { @@ -1200,68 +1190,63 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; } + NSUInteger suffixLabelStart = line.length; [line appendAttributedString: [[NSAttributedString alloc] initWithString:labelString attributes:labelAttrs]]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); + } } if (i < comments.count && [comments[i] length] != 0) { - CGFloat candidateAndLabelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + NSUInteger commentStart = line.length; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:@" " + attributes:commentAttrs]]; NSString *comment = comments[i]; - NSAttributedString *commentAttributedString = [[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]; - CGFloat commentWidth = [commentAttributedString boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; - if (commentWidth <= maxTextWidth * 0.2) { - // Unicode Word Joiner - commentAttributedString = insert(@"\u2060", commentAttributedString); - } - - NSString *commentSeparator; - if (candidateAndLabelWidth + commentWidth <= maxTextWidth * 0.3) { - // Non-Breaking White Space - commentSeparator = @"\u00A0"; - } else { - commentSeparator = @" "; - } [line appendAttributedString:[[NSAttributedString alloc] - initWithString:commentSeparator + initWithString:comment.precomposedStringWithCanonicalMapping attributes:commentAttrs]]; - [line appendAttributedString:commentAttributedString]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); + } } NSAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:attrs]; - - NSMutableAttributedString *str = [separator mutableCopy]; - if (theme.vertical) { - [str addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:NSMakeRange(0, str.length)]; - } - _view.seperatorWidth = [str boundingRectWithSize:NSZeroSize options:0].size.width; + _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; - NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + NSMutableParagraphStyle *paragraphStyleCandidate; if (i == 0) { -// paragraphStyleCandidate.lineSpacing = theme.linespace + theme.hilitedCornerRadius; - paragraphStyleCandidate.paragraphSpacingBefore = theme.preeditLinespace + theme.hilitedCornerRadius; + NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; + firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; + paragraphStyleCandidate = firstParagraphStyle; } else { + paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; [text appendAttributedString:separator]; } if (!theme.linear) { // paragraphStyleCandidate.lineSpacing = theme.linespace; paragraphStyleCandidate.headIndent = labelWidth; } + if (theme.vertical) { + paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); + } // Use left-to-right marks to declare the default writing direction and // prevent strong right-to-left characters from setting the wirint direction paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; + paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; - NSRange candidateRange = NSMakeRange(text.length, line.length); - [candidateRanges addObject: [NSValue valueWithRange:candidateRange]]; + if (i == index) { + highlightedRange = NSMakeRange(text.length, line.length); + } [text appendAttributedString:line]; } @@ -1269,13 +1254,8 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(text); // text done! - [_view.textView.textStorage setAttributedString:text]; - if (theme.vertical) { - _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; - } else { - _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; - } - [_view drawViewWith:candidateRanges hilightedIndex:index preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view setText:text]; + [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; } @@ -1285,19 +1265,13 @@ - (void)updateStatus:(NSString *)message { - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.attrs]; - [text addAttribute:NSParagraphStyleAttributeName - value:theme.paragraphStyle - range:NSMakeRange(0, text.length)]; - fixDefaultFont(text); - [_view.textView.textStorage setAttributedString:text]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; if (theme.vertical) { - _view.textView.layoutOrientation = NSTextLayoutOrientationVertical; - } else { - _view.textView.layoutOrientation = NSTextLayoutOrientationHorizontal; + convertToVerticalGlyph(text, NSMakeRange(0, text.length)); } + [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:[[NSArray alloc] init] hilightedIndex:0 preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1396,21 +1370,15 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; - BOOL mutualExclusive = [config getBool:@"style/mutual_exclusive"]; - NSNumber *memorizeSizeConfig = [config getOptionalBool:@"style/memorize_size"]; - if (memorizeSizeConfig) { - theme.memorizeSize = memorizeSizeConfig.boolValue; - } - NSString *candidateFormat = [config getString:@"style/candidate_format"]; + NSString *fontName = [config getString:@"style/font_face"]; - CGFloat fontSize = [config getDouble:@"style/font_point"]; + NSInteger fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; + NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; - NSNumber *alphaValue = [config getOptionalDouble:@"style/alpha"]; - CGFloat alpha = alphaValue ? fmin(fmax(alphaValue.doubleValue, 0.0), 1.0) : 1.0; + NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; + CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; @@ -1418,7 +1386,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; - CGFloat shadowSize = fmax(0, [config getDouble:@"style/shadow_size"]); NSColor *backgroundColor; NSColor *borderColor; @@ -1465,16 +1432,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo // if not otherwise specified, candidate text is also rendered in this color. candidateTextColor = textColor; } - candidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/label_color"]]; - highlightedCandidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (!highlightedCandidateLabelColor) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' - // are both valid - highlightedCandidateLabelColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; - } highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; if (highlightedCandidateTextColor == nil) { @@ -1511,11 +1468,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } - NSNumber *mutualExclusiveOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/mutual_exclusive"]]; - if (mutualExclusiveOverridden) { - mutualExclusive = mutualExclusiveOverridden.boolValue; - } NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { @@ -1552,6 +1504,22 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSizeOverridden) { commentFontSize = commentFontSizeOverridden.integerValue; } + NSColor *candidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/label_color"]]; + if (candidateLabelColorOverridden) { + candidateLabelColor = candidateLabelColorOverridden; + } + NSColor *highlightedCandidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; + if (!highlightedCandidateLabelColorOverridden) { + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' + // are both valid + highlightedCandidateLabelColorOverridden = + [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; + } + if (highlightedCandidateLabelColorOverridden) { + highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; + } NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { @@ -1592,11 +1560,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } - NSNumber *shadowSizeOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/shadow_size"]]; - if (shadowSizeOverridden) { - shadowSize = shadowSizeOverridden.doubleValue; - } } if (fontSize == 0) { // default size @@ -1746,21 +1709,19 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSSize edgeInset; if (vertical) { - edgeInset = NSMakeSize(borderHeight + cornerRadius, borderWidth + cornerRadius); + edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); } else { - edgeInset = NSMakeSize(borderWidth + cornerRadius, borderHeight + cornerRadius); + edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); } [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius - shadowSize:shadowSize edgeInset:edgeInset borderWidth:MIN(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing - alpha:alpha + alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency - mutualExclusive:mutualExclusive linear:linear vertical:vertical inlinePreedit:inlinePreedit diff --git a/librime b/librime index 020724e45..62333d727 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 020724e45ccaf7756fa1f8104399d614efdb068f +Subproject commit 62333d727ed74a095023aba77f6fcb5f96b4f514 diff --git a/utf8.cpp b/utf8.cpp index ebdd5bfd0..24c6590b1 100644 --- a/utf8.cpp +++ b/utf8.cpp @@ -1,10 +1,7 @@ #include "utf8.h" #include "utf8/unchecked.h" -#include unsigned long utf8len(const char *text, unsigned octet_len) { - std::vector utf16result; - utf8::unchecked::utf8to16(text, text + octet_len, std::back_inserter(utf16result)); - return utf16result.size(); + return utf8::unchecked::distance(text, text + octet_len); } From b00c84fcb365f7f81b990e3a843597689c5863ff Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 28 Mar 2023 01:12:14 +0200 Subject: [PATCH 038/164] UI --- SquirrelInputController.m | 4 +- SquirrelPanel.m | 144 ++++++++++++++++++++++---------------- librime | 2 +- 3 files changed, 85 insertions(+), 65 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 657291a24..10d4e6163 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -351,7 +351,7 @@ -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { - if ([_preeditString isEqualToString:@" "]) { + if ([_preeditString isEqualToString:@"\u3000"]) { const char* raw_input = rime_get_api()->get_input(_session); if (raw_input) [self commitString:@(raw_input)]; @@ -584,7 +584,7 @@ -(void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; + [self showPreeditString:(preedit ? @"\u3000" : @"") selRange:empty caretPos:0]; } } // update candidates diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 51506ba3a..a800ac54d 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -130,7 +130,7 @@ @implementation SquirrelTheme - (void)setCandidateFormat:(NSString *)candidateFormat { // in the candiate format, everything other than '%@' is considered part of the label - NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; + NSRange candidateRange = [candidateFormat rangeOfString:@"%@" options:NSLiteralSearch]; if (candidateRange.location == NSNotFound) { _prefixLabelFormat = candidateFormat; _suffixLabelFormat = nil; @@ -322,7 +322,7 @@ - (void)drawViewWith:(NSRange)hilightedRange return 1; } else if (number <= -2) { return -1; - }else { + } else { return number / 2; } } @@ -510,8 +510,8 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -// Add gap between horizontal candidates -- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { +// Add gap between linear candidates +- (void)addGapBetweenLinearCandidates:(NSRect *)rect { if (_highlightedRange.location+_highlightedRange.length == _text.length) { if (!nearEmptyRect(*rect)) { rect->size.width += _seperatorWidth / 2; @@ -587,9 +587,9 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect trailingRect; [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - [self addGapBetweenHorizontalCandidates:&leadingRect]; - [self addGapBetweenHorizontalCandidates:&bodyRect]; - [self addGapBetweenHorizontalCandidates:&trailingRect]; + [self addGapBetweenLinearCandidates:&leadingRect]; + [self addGapBetweenLinearCandidates:&bodyRect]; + [self addGapBetweenLinearCandidates:&trailingRect]; NSMutableArray *highlightedPoints; NSMutableArray *highlightedPoints2; @@ -817,11 +817,11 @@ void fixDefaultFont(NSMutableAttributedString *text) { long i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; - NSNumber *baselineOffset = @(charFont.pointSize * -0.1); +// NSNumber *baselineOffset = @(charFont.pointSize * -0.1); if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; - [text addAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:currentFontRange]; +// [text addAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:currentFontRange]; } i = currentFontRange.location + currentFontRange.length; } @@ -965,7 +965,7 @@ - (void)show { NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) < 0.5) || (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; @@ -979,10 +979,10 @@ - (void)show { windowRect.size = NSMakeSize(contentRect.size.height + theme.edgeInset.height * 2, contentRect.size.width + theme.edgeInset.width * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMidY(_position) / NSHeight(_screenRect) >= 0.5) { + if (NSMinY(_position) / NSHeight(_screenRect) >= 0.5) { windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + kOffsetHeight; + windowRect.origin.y = NSMaxY(_position) + kOffsetHeight + theme.edgeInset.width * 3; } // Make the first candidate fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - windowRect.size.width - kOffsetHeight; @@ -997,21 +997,25 @@ - (void)show { NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } - if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { // can only happen with horizontal - windowRect.origin.x = NSMinX(_position) - NSWidth(windowRect) - kOffsetHeight; + if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { + windowRect.origin.x = MIN(NSMinX(_position) - kOffsetHeight, NSMaxX(_screenRect)) - NSWidth(windowRect); } - if (NSMinX(windowRect) < NSMinX(_screenRect)) { // can only happen with vertical - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + if (NSMinX(windowRect) < NSMinX(_screenRect)) { + if (theme.vertical) { + windowRect.origin.x = MAX(NSMaxX(_position) + kOffsetHeight + theme.edgeInset.height * 3, NSMinX(_screenRect)); + } else { + windowRect.origin.x = MAX(NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width * 3, NSMinX(_screenRect)); + } } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - if (theme.vertical) { // failed attempt: NSMaxY(_position) + kOffsetHeight; - windowRect.origin.y = NSMinY(_screenRect); + if (theme.vertical) { + windowRect.origin.y = MAX(NSMaxY(_position) + kOffsetHeight + theme.edgeInset.width * 3, NSMinY(_screenRect)); } else { - windowRect.origin.y = NSMaxY(_position) + kOffsetHeight; + windowRect.origin.y = MAX(NSMaxY(_position) + kOffsetHeight + theme.edgeInset.height * 3, NSMinY(_screenRect)); } } - if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { // failed attempt: NSMaxY(_position) + kOffsetHeight; - windowRect.origin.y = NSMaxY(_screenRect) - NSHeight(windowRect); + if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { + windowRect.origin.y = MIN(NSMinY(_position) - kOffsetHeight, NSMaxY(_screenRect)) - NSHeight(windowRect); } [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! @@ -1109,6 +1113,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; if (theme.vertical) { convertToVerticalGlyph(text, NSMakeRange(0, line.length)); + // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, line.length)]; paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); } [text addAttribute:NSParagraphStyleAttributeName @@ -1135,7 +1140,7 @@ - (void)showPreedit:(NSString *)preedit (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; - CGFloat labelWidth = 0.0; + NSSize labelSize = NSMakeSize(0, 0); if (theme.prefixLabelFormat != nil) { NSString *labelString; @@ -1156,12 +1161,13 @@ - (void)showPreedit:(NSString *)preedit [[NSAttributedString alloc] initWithString:labelString attributes:labelAttrs]]; - // get the label size for indent if (theme.vertical) { convertToVerticalGlyph(line, NSMakeRange(0, line.length)); + // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, line.length)]; } + // get the label size for indent if (!theme.linear) { - labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + labelSize = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size; } } @@ -1173,7 +1179,23 @@ - (void)showPreedit:(NSString *)preedit // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); +// convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); + [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(candidateStart, line.length-candidateStart)]; + } + + if (i < comments.count && [comments[i] length] != 0) { + NSUInteger commentStart = line.length; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:@" " + attributes:commentAttrs]]; + NSString *comment = comments[i]; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:comment.precomposedStringWithCanonicalMapping + attributes:commentAttrs]]; + if (theme.vertical) { + convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); + // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(commentStart, line.length-commentStart)]; + } } if (theme.suffixLabelFormat != nil) { @@ -1197,27 +1219,14 @@ - (void)showPreedit:(NSString *)preedit attributes:labelAttrs]]; if (theme.vertical) { convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); - } - } - - if (i < comments.count && [comments[i] length] != 0) { - NSUInteger commentStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; - NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); + // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)]; } } NSAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") - attributes:attrs]; - _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; + attributes:labelAttrs]; + _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; NSMutableParagraphStyle *paragraphStyleCandidate; if (i == 0) { @@ -1226,20 +1235,26 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate = firstParagraphStyle; } else { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + NSUInteger separatorStart = text.length; [text appendAttributedString:separator]; + if (theme.vertical) { + [text addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(separatorStart, text.length-separatorStart)]; + } } - if (!theme.linear) { -// paragraphStyleCandidate.lineSpacing = theme.linespace; - paragraphStyleCandidate.headIndent = labelWidth; + if (theme.linear) { + paragraphStyleCandidate.lineSpacing = theme.linespace; +// paragraphStyleCandidate.headIndent = labelWidth; } if (theme.vertical) { paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); + paragraphStyleCandidate.headIndent = labelSize.height; + } else { + paragraphStyleCandidate.headIndent = labelSize.width; } // Use left-to-right marks to declare the default writing direction and // prevent strong right-to-left characters from setting the wirint direction paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; - paragraphStyleCandidate.headIndent = labelWidth; [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; @@ -1254,6 +1269,9 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(text); // text done! + // if (theme.vertical) { + // [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; + // } [_view setText:text]; [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; @@ -1268,6 +1286,8 @@ - (void)showStatus:(NSString *)message { NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; if (theme.vertical) { convertToVerticalGlyph(text, NSMakeRange(0, text.length)); + // [text addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, text.length)]; + // [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; } [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); @@ -1373,11 +1393,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSString *candidateFormat = [config getString:@"style/candidate_format"]; NSString *fontName = [config getString:@"style/font_face"]; - NSInteger fontSize = [config getDouble:@"style/font_point"]; + CGFloat fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; + CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; + CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; @@ -1482,7 +1502,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *fontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; if (fontSizeOverridden) { - fontSize = fontSizeOverridden.integerValue; + fontSize = fontSizeOverridden.doubleValue; } NSString *labelFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; @@ -1492,7 +1512,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *labelFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; if (labelFontSizeOverridden) { - labelFontSize = labelFontSizeOverridden.integerValue; + labelFontSize = labelFontSizeOverridden.doubleValue; } NSString *commentFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; @@ -1502,7 +1522,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *commentFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; if (commentFontSizeOverridden) { - commentFontSize = commentFontSizeOverridden.integerValue; + commentFontSize = commentFontSizeOverridden.doubleValue; } NSColor *candidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_color"]]; @@ -1621,23 +1641,23 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } } - CGFloat lineHeight = fmax(fmax(fontSize, labelFontSize), commentFontSize) * 1.15; + // CGFloat lineHeight = fmax(fmax(fontSize, labelFontSize), commentFontSize) * 1.3; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing; - paragraphStyle.maximumLineHeight = lineHeight + lineSpacing; -// paragraphStyle.paragraphSpacing = lineSpacing / 2; -// paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; - paragraphStyle.lineSpacing = lineSpacing; + // paragraphStyle.minimumLineHeight = lineHeight + lineSpacing; + // paragraphStyle.maximumLineHeight = lineHeight + lineSpacing; + paragraphStyle.paragraphSpacing = lineSpacing / 2; + paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + // paragraphStyle.lineSpacing = lineSpacing; +// paragraphStyle.defaultTabInterval = fontSize * 1.2; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - preeditParagraphStyle.minimumLineHeight = fontSize * 1.15 + spacing; - preeditParagraphStyle.maximumLineHeight = fontSize * 1.15 + spacing; -// preeditParagraphStyle.paragraphSpacing = spacing + hilitedCornerRadius - lineSpacing; - preeditParagraphStyle.lineSpacing = spacing; - preeditParagraphStyle.paragraphSpacing = hilitedCornerRadius; + // preeditParagraphStyle.minimumLineHeight = fontSize * 1.3 + spacing; + // preeditParagraphStyle.maximumLineHeight = fontSize * 1.3 + spacing; + preeditParagraphStyle.paragraphSpacing = spacing / 2 + hilitedCornerRadius / 2; + // preeditParagraphStyle.lineSpacing = spacing; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; diff --git a/librime b/librime index 62333d727..4d13bc1df 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 62333d727ed74a095023aba77f6fcb5f96b4f514 +Subproject commit 4d13bc1dff6374b1c4aae43912599ebb2c791059 From b5b3ec98e97ba1f82d6d83088684019c2a229cb0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 30 Mar 2023 15:10:15 +0200 Subject: [PATCH 039/164] Support Clear --- macos_keycode.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos_keycode.m b/macos_keycode.m index a4437a324..c59b56981 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -91,7 +91,7 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) {OSX_VK_KEYPAD_7, XK_KP_7}, {OSX_VK_KEYPAD_8, XK_KP_8}, {OSX_VK_KEYPAD_9, XK_KP_9}, - //OSX_VK_KEYPAD_CLEAR -> ? + {OSX_VK_KEYPAD_CLEAR, XK_Clear}, {OSX_VK_KEYPAD_COMMA, XK_KP_Separator}, {OSX_VK_KEYPAD_DOT, XK_KP_Decimal}, {OSX_VK_KEYPAD_EQUAL, XK_KP_Equal}, From 8747824efe23ec5a3d5ca8ddbc3699657e201688 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 5 Apr 2023 15:45:16 +0200 Subject: [PATCH 040/164] Misc --- SquirrelInputController.m | 7 +- SquirrelPanel.m | 209 ++++++++++++++++++-------------------- librime | 2 +- macos_keycode.h | 8 -- macos_keycode.m | 24 ++--- utf8.cpp | 6 +- 6 files changed, 121 insertions(+), 135 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 10d4e6163..cdb0b1dbc 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -138,6 +138,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; + _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -146,7 +147,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender int keyCode = event.keyCode; NSString* keyChars = event.charactersIgnoringModifiers; - if (!isalpha(keyChars.UTF8String[0])) { + if ((modifiers & OSX_SHIFT_MASK) && + !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) { keyChars = event.characters; } //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", @@ -154,7 +156,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender // translate osx keyevents to rime keyevents int rime_keycode = osx_keycode_to_rime_keycode(keyCode, keyChars.UTF8String[0], - modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); + modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) @@ -177,7 +179,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } } - _lastModifier = modifiers; _lastEventType = event.type; return handled; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a800ac54d..cab0c79ad 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -233,8 +233,8 @@ @interface SquirrelView : NSView - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; - (void)setText:(NSAttributedString *)text; -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange +- (void) drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -307,9 +307,9 @@ - (void)setText:(NSAttributedString *)text { } // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { +- (void) drawViewWith:(NSRange)hilightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange { _highlightedRange = hilightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; @@ -777,7 +777,7 @@ - (BOOL)inlineCandidate { CGFloat minimumHeight(NSDictionary *attribute) { const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; - const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; + const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:(NSStringDrawingUsesLineFragmentOrigin)].size.height; return minimumHeight; } @@ -789,19 +789,19 @@ void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange str // Use the width of the character to determin if they should be upright in vertical writing mode. // Adjust font base line for better alignment. const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; - const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; + const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; - const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; + const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size; stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; NSUInteger i = stringRange.location; while (i < stringRange.location+stringRange.length) { NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; i = range.location + range.length; - NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; // Also adjust the baseline so upright and lying charcters are properly aligned if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; - NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; + NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; CGFloat widthDiff = charRect.size.width-cjkChar.size.width; CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; @@ -811,17 +811,23 @@ void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange str } } -void fixDefaultFont(NSMutableAttributedString *text) { +void fixDefaultFont(NSMutableAttributedString *text, BOOL vertical) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); long i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; -// NSNumber *baselineOffset = @(charFont.pointSize * -0.1); + NSMutableParagraphStyle *paragraphStyle = [text attribute:NSParagraphStyleAttributeName atIndex:i effectiveRange:¤tFontRange]; if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; +// CGFloat fontHeight = NSHeight([defaultFont boundingRectForFont]); [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; -// [text addAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:currentFontRange]; +// if ([[text attribute:NSTextLayoutSectionOrientation atIndex:i effectiveRange:¤tFontRange] isEqualToValue:@(NSTextLayoutOrientationVertical)]) + if (vertical) { + [text addAttribute:NSBaselineOffsetAttributeName value:@(-charFont.pointSize/10) range:currentFontRange]; +// paragraphStyle.lineHeightMultiple = 4/3; +// [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:currentFontRange]; + } } i = currentFontRange.location + currentFontRange.length; } @@ -965,7 +971,7 @@ - (void)show { NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) < 0.5) || + if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; @@ -975,47 +981,51 @@ - (void)show { } } + // the sweep direction of the client app changes the behavior of adjusting squirrel panel position + bool sweepVertical = NSWidth(_position) > NSHeight(_position); if (theme.vertical) { windowRect.size = NSMakeSize(contentRect.size.height + theme.edgeInset.height * 2, contentRect.size.width + theme.edgeInset.width * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) / NSHeight(_screenRect) >= 0.5) { - windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); + if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { + windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : kOffsetHeight) - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + kOffsetHeight + theme.edgeInset.width * 3; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight) + theme.edgeInset.width; } // Make the first candidate fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - windowRect.size.width - kOffsetHeight; - if (_preeditRange.length > 0) { + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); + if (!sweepVertical && _preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_preeditRange].size; windowRect.origin.x += preeditSize.height + theme.edgeInset.width; } } else { windowRect.size = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, contentRect.size.height + theme.edgeInset.height * 2); - windowRect.origin = NSMakePoint(NSMinX(_position), - NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + if (sweepVertical) { + // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa + if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + } else { + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width; + } + windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); + } else { + windowRect.origin = NSMakePoint(NSMinX(_position), + NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + } } if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { - windowRect.origin.x = MIN(NSMinX(_position) - kOffsetHeight, NSMaxX(_screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(_screenRect)) { - if (theme.vertical) { - windowRect.origin.x = MAX(NSMaxX(_position) + kOffsetHeight + theme.edgeInset.height * 3, NSMinX(_screenRect)); - } else { - windowRect.origin.x = MAX(NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width * 3, NSMinX(_screenRect)); - } + windowRect.origin.x = (sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(_screenRect)) + (theme.vertical ? theme.edgeInset.height : theme.edgeInset.width); } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - if (theme.vertical) { - windowRect.origin.y = MAX(NSMaxY(_position) + kOffsetHeight + theme.edgeInset.width * 3, NSMinY(_screenRect)); - } else { - windowRect.origin.y = MAX(NSMaxY(_position) + kOffsetHeight + theme.edgeInset.height * 3, NSMinY(_screenRect)); - } + windowRect.origin.y = (sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position) + kOffsetHeight) + (theme.vertical ? theme.edgeInset.width : theme.edgeInset.height); } if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { - windowRect.origin.y = MIN(NSMinY(_position) - kOffsetHeight, NSMaxY(_screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); } [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! @@ -1079,6 +1089,13 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; + NSFont *currentFont = theme.attrs[NSFontAttributeName]; + CGFloat fontScale = currentFont.pointSize / 12; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + CGFloat maxTextWidth = theme.vertical + ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; _preeditRange = NSMakeRange(NSNotFound, 0); @@ -1087,50 +1104,41 @@ - (void)showPreedit:(NSString *)preedit if (preedit) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { NSUInteger highlightedPreeditStart = line.length; - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping - attributes:theme.preeditHighlightedAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping + attributes:theme.preeditHighlightedAttrs]]; highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, line.length - highlightedPreeditStart); } if (selRange.location + selRange.length < preedit.length) { - [line - appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringFromIndex:selRange.location + - selRange.length].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringFromIndex:selRange.location + selRange.length].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } [text appendAttributedString:line]; NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; - if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, line.length)); - // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, line.length)]; - paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); - } + paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePreedit range:NSMakeRange(0, text.length)]; _preeditRange = NSMakeRange(0, text.length); if (numCandidates) { - [text appendAttributedString:[[NSAttributedString alloc] - initWithString:@"\n" - attributes:theme.preeditAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" + attributes:theme.preeditAttrs]]; } candidateStartPos = text.length; } NSRange highlightedRange = NSMakeRange(NSNotFound, 0); // candidates + CGFloat textWidth = 0.0; NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; @@ -1140,7 +1148,7 @@ - (void)showPreedit:(NSString *)preedit (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; - NSSize labelSize = NSMakeSize(0, 0); + CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { NSString *labelString; @@ -1157,45 +1165,28 @@ - (void)showPreedit:(NSString *)preedit labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; } - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:labelString - attributes:labelAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(0, line.length)); - // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, line.length)]; - } + [line appendAttributedString:[[NSAttributedString alloc] initWithString:labelString + attributes:labelAttrs]]; // get the label size for indent if (!theme.linear) { - labelSize = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size; + labelWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; - if (theme.vertical) { -// convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); - [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(candidateStart, line.length-candidateStart)]; - } if (i < comments.count && [comments[i] length] != 0) { NSUInteger commentStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " + attributes:commentAttrs]]; NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); - // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(commentStart, line.length-commentStart)]; - } + [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCanonicalMapping + attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { @@ -1213,19 +1204,26 @@ - (void)showPreedit:(NSString *)preedit labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; } NSUInteger suffixLabelStart = line.length; - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:labelString - attributes:labelAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); - // [line addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)]; - } + [line appendAttributedString:[[NSAttributedString alloc] initWithString:labelString + attributes:labelAttrs]]; } - NSAttributedString *separator = [[NSMutableAttributedString alloc] - initWithString:(theme.linear ? @" " : @"\n") - attributes:labelAttrs]; + // determine if the line is too wide and line break is needed, based on screen size. + NSString *separtatorString = @"\u2029"; + if (theme.linear) { + CGFloat lineWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + CGFloat spaceWidth = [[NSMutableAttributedString alloc] initWithString: @" " attributes:attrs].size.width; + textWidth += lineWidth + spaceWidth; + if (textWidth > maxTextWidth) { + separtatorString = @"\u2028"; + textWidth = lineWidth; + } else { + separtatorString = @" "; + } + } + + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString + attributes:attrs]; _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; NSMutableParagraphStyle *paragraphStyleCandidate; @@ -1237,20 +1235,13 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; NSUInteger separatorStart = text.length; [text appendAttributedString:separator]; - if (theme.vertical) { - [text addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(separatorStart, text.length-separatorStart)]; - } } if (theme.linear) { paragraphStyleCandidate.lineSpacing = theme.linespace; -// paragraphStyleCandidate.headIndent = labelWidth; - } - if (theme.vertical) { - paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); - paragraphStyleCandidate.headIndent = labelSize.height; } else { - paragraphStyleCandidate.headIndent = labelSize.width; + paragraphStyleCandidate.headIndent = labelWidth; } + paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs) + theme.linespace; // Use left-to-right marks to declare the default writing direction and // prevent strong right-to-left characters from setting the wirint direction paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; @@ -1266,12 +1257,13 @@ - (void)showPreedit:(NSString *)preedit } // Fix font rendering - fixDefaultFont(text); + if (theme.vertical) { + [text addAttribute:NSVerticalGlyphFormAttributeName value:@YES range:NSMakeRange(0, text.length)]; + [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; + } + fixDefaultFont(text, theme.vertical); // text done! - // if (theme.vertical) { - // [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; - // } [_view setText:text]; [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; [self show]; @@ -1285,9 +1277,8 @@ - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, text.length)); - // [text addAttribute:NSVerticalGlyphFormAttributeName value:@true range:NSMakeRange(0, text.length)]; - // [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; + [text addAttribute:NSVerticalGlyphFormAttributeName value:@YES range:NSMakeRange(0, text.length)]; + [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; } [_view setText:text]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); diff --git a/librime b/librime index 4d13bc1df..b239cb465 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 4d13bc1dff6374b1c4aae43912599ebb2c791059 +Subproject commit b239cb465a70970110b5179e147dd3d685b3a75e diff --git a/macos_keycode.h b/macos_keycode.h index db17ceaac..e24e44375 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -3,14 +3,6 @@ #define _MACOS_KEYCODE_H_ // masks -#define OSX_LEFT_CTRL_MASK 1 << 0 -#define OSX_LEFT_SHIFT_MASK 1 << 1 -#define OSX_RIGHT_SHIFT_MASK 1 << 2 -#define OSX_LEFT_COMMAND_MASK 1 << 3 -#define OSX_RIGHT_COMMAND_MASK 1 << 4 -#define OSX_LEFT_ALT_MASK 1 << 5 -#define OSX_RIGHT_ALT_MASK 1 << 6 -#define OSX_RIGHT_CTRL_MASK 1 << 13 #define OSX_CAPITAL_MASK 1 << 16 #define OSX_SHIFT_MASK 1 << 17 #define OSX_CTRL_MASK 1 << 18 diff --git a/macos_keycode.m b/macos_keycode.m index c59b56981..780a5be56 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -131,18 +131,18 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) if (keychar >= 0x20 && keychar <= 0x7e) { return keychar; } - else if (keychar == 0x1b) { // ^[ - return XK_bracketleft; - } - else if (keychar == 0x1c) { // ^\ - return XK_backslash; - } - else if (keychar == 0x1d) { // ^] - return XK_bracketright; - } - else if (keychar == 0x1f) { // ^_ - return XK_minus; - } +// else if (keychar == 0x1b) { // ^[ +// return XK_bracketleft; +// } +// else if (keychar == 0x1c) { // ^\ +// return XK_backslash; +// } +// else if (keychar == 0x1d) { // ^] +// return XK_bracketright; +// } +// else if (keychar == 0x1f) { // ^_ +// return XK_minus; +// } return XK_VoidSymbol; } diff --git a/utf8.cpp b/utf8.cpp index 24c6590b1..786e04b96 100644 --- a/utf8.cpp +++ b/utf8.cpp @@ -1,7 +1,9 @@ #include "utf8.h" #include "utf8/unchecked.h" - +#include unsigned long utf8len(const char *text, unsigned octet_len) { - return utf8::unchecked::distance(text, text + octet_len); + std::vector utf16result; + utf8::unchecked::utf8to16(text, text + octet_len, std::back_inserter(utf16result)); + return utf16result.size(); } From 52ed9e8c5df4bb6fcd87d659fcaa19e5e47f032f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 6 Apr 2023 15:11:39 +0200 Subject: [PATCH 041/164] UI --- SquirrelInputController.m | 12 +- SquirrelPanel.h | 4 +- SquirrelPanel.m | 643 +++++++++++++++++++------------------- 3 files changed, 331 insertions(+), 328 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index cdb0b1dbc..3243e27d1 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -90,6 +90,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } int release_mask = 0; NSUInteger changes = _lastModifier ^ modifiers; + _lastModifier = modifiers; if (changes & OSX_CAPITAL_MASK) { if (!keyCodeAvailable) { rime_keycode = XK_Caps_Lock; @@ -138,7 +139,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; - _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -459,6 +459,8 @@ -(void)showPanelWithPreedit:(NSString*)preedit comments:(NSArray*)comments labels:(NSArray*)labels highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; @@ -472,7 +474,9 @@ -(void)showPanelWithPreedit:(NSString*)preedit candidates:candidates comments:comments labels:labels - highlighted:index]; + highlighted:index + pageNum:pageNum + lastPage:lastPage]; } @end // SquirrelController @@ -620,7 +624,9 @@ -(void)rimeUpdate candidates:candidates comments:comments labels:labels - highlighted:ctx.menu.highlighted_candidate_index]; + highlighted:ctx.menu.highlighted_candidate_index + pageNum:ctx.menu.page_no + lastPage:ctx.menu.is_last_page]; rime_get_api()->free_context(&ctx); } else { [NSApp.squirrelAppDelegate.panel hide]; diff --git a/SquirrelPanel.h b/SquirrelPanel.h index f3b1b73f7..dcabd468e 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -22,7 +22,9 @@ candidates:(NSArray*)candidates comments:(NSArray*)comments labels:(NSArray*)labels - highlighted:(NSUInteger)index; + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage; -(void)hide; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index cab0c79ad..20fbf37f1 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -86,8 +86,10 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSDictionary *commentHighlightedAttrs; @property(nonatomic, strong, readonly) NSDictionary *preeditAttrs; @property(nonatomic, strong, readonly) NSDictionary *preeditHighlightedAttrs; +@property(nonatomic, strong, readonly) NSDictionary *pagingAttrs; @property(nonatomic, strong, readonly) NSParagraphStyle *paragraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; +@property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; @property(nonatomic, strong, readonly) NSString *prefixLabelFormat, *suffixLabelFormat; @@ -119,10 +121,12 @@ - (void) setAttrs:(NSMutableDictionary *)attrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle; @end @@ -199,7 +203,8 @@ - (void) setAttrs:(NSMutableDictionary *)attrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs { _attrs = attrs; _labelAttrs = labelAttrs; _highlightedAttrs = highlightedAttrs; @@ -208,22 +213,26 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _commentHighlightedAttrs = commentHighlightedAttrs; _preeditAttrs = preeditAttrs; _preeditHighlightedAttrs = preeditHighlightedAttrs; + _pagingAttrs = pagingAttrs; } - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle { + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle { _paragraphStyle = paragraphStyle; _preeditParagraphStyle = preeditParagraphStyle; + _pagingParagraphStyle = pagingParagraphStyle; } @end @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextStorage *text; +@property(nonatomic, readonly) NSTextView *textView; @property(nonatomic, readonly) NSRange highlightedRange; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; +@property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @@ -232,10 +241,10 @@ @interface SquirrelView : NSView - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; -- (void)setText:(NSAttributedString *)text; - (void) drawViewWith:(NSRange)hilightedRange preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -272,47 +281,48 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } + _textView = [[NSTextView alloc] initWithFrame:frameRect]; // Use textStorage to store text and manage all text layout and draws NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - _text = [[NSTextStorage alloc] init]; - [_text addLayoutManager:layoutManager]; - layoutManager.backgroundLayoutEnabled = YES; + _textView.drawsBackground = NO; + _textView.selectable = NO; + [_textView replaceTextContainer:textContainer]; + _textView.layoutManager.backgroundLayoutEnabled = YES; + _textView.layoutManager.usesFontLeading = NO; + _textView.layoutManager.typesetterBehavior = NSTypesetterBehavior_10_4; _defaultTheme = [[SquirrelTheme alloc] init]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; +// _textView.usesAdaptiveColorMappingForDarkAppearance = YES; } return self; } // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } -- (void)setText:(NSAttributedString *)text { - [_text setAttributedString:[text copy]]; -} - // Will triger - (void)drawRect:(NSRect)dirtyRect - (void) drawViewWith:(NSRange)hilightedRange preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange { _highlightedRange = hilightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; + _pagingRange = pagingRange; self.needsDisplay = YES; } @@ -401,8 +411,8 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _text.layoutManagers[0]; - NSTextContainer *textContainer = layoutManager.textContainers[0]; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; @@ -419,18 +429,18 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe leadingRect->origin.x = rightEdge; leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; } - if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { + if (NSMaxRange(fullRangeInBoundingRect) > NSMaxRange(glyphRange)) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) - inTextContainer:textContainer]; + NSMakeRange(NSMaxRange(glyphRange), NSMaxRange(fullRangeInBoundingRect)-NSMaxRange(glyphRange)) + inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; } double leftEdge = NSMinX(*trailingRect); trailingRect->origin.x = bodyRect->origin.x; trailingRect->size.width = leftEdge - bodyRect->origin.x; - } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { - *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; + } else if (NSMaxRange(fullRangeInBoundingRect) == NSMaxRange(glyphRange)) { + *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { *trailingRect = NSZeroRect; } else if (!nearEmptyRect(*trailingRect)) { @@ -438,14 +448,14 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } } NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; - lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; + lastLineRect.size.width = textContainer.size.width - lastLineRect.origin.x; NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; - NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; - while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { + NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; + while (lastLineRange.length>0 && (glyphProperty & NSGlyphPropertyElastic && glyphProperty & NSGlyphPropertyControlCharacter)) { lastLineRange.length -= 1; - glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; } - if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { + if (NSMaxRange(lastLineRange) == NSMaxRange(glyphRange)) { if (!nearEmptyRect(*trailingRect)) { *trailingRect = lastLineRect; } else { @@ -510,25 +520,6 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -// Add gap between linear candidates -- (void)addGapBetweenLinearCandidates:(NSRect *)rect { - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; - rect->origin.x -= _seperatorWidth / 2; - } - } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; - } - } else { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth; - rect->origin.x -= _seperatorWidth / 2; - } - } -} - // All draws happen here - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *backgroundPath; @@ -538,59 +529,73 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *highlightedPreeditPath; NSBezierPath *highlightedPreeditPath2; NSBezierPath *preeditPath; + NSBezierPath *pagingPath; + NSBezierPath *candidatePath; SquirrelTheme * theme = self.currentTheme; - - NSRect textField = dirtyRect; - textField.origin.y += theme.edgeInset.height; - textField.origin.x += theme.edgeInset.width; - - // Draw preedit Rect + + [NSBezierPath setDefaultLineWidth:0]; NSRect backgroundRect = dirtyRect; - + NSRect textField = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); + // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = textField.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); + preeditRect.size.width = backgroundRect.size.width; + preeditRect.size.height += theme.edgeInset.height/2 + theme.preeditLinespace; + preeditRect.origin = backgroundRect.origin; if (_highlightedRange.length == 0) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; + preeditRect.size.height += theme.edgeInset.height*3/2 - theme.preeditLinespace; } if (theme.preeditBackgroundColor != nil) { preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); } } - // Draw highlighted Rect - if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + // Draw paging Rect + NSRect pagingRect = NSZeroRect; + if (_pagingRange.length > 0) { + pagingRect = [self contentRectForRange:_pagingRange]; + pagingRect.size.width = backgroundRect.size.width; + pagingRect.size.height += theme.edgeInset.height/2; + pagingRect.origin.x = backgroundRect.origin.x; + pagingRect.origin.y += theme.edgeInset.height*3/2; + if (theme.preeditBackgroundColor != nil) { + pagingPath = drawSmoothLines(rectVertex(pagingRect), 0, 0); } - NSRect outerBox = backgroundRect; - outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; + } + + // Draw candidate Rect + CGFloat halfLinespace = theme.linespace / 2; + NSRect candidateRect = backgroundRect; + candidateRect.size.height -= preeditRect.size.height + pagingRect.size.height; + candidateRect.origin.y += preeditRect.size.height; + if (_preeditRange.length == 0) { + candidateRect.size.height -= theme.edgeInset.height/2; + candidateRect.origin.y += theme.edgeInset.height/2; + } + + NSRect outerBox = NSInsetRect(candidateRect, theme.edgeInset.width, theme.edgeInset.height/2); + NSRect innerBox = NSInsetRect(outerBox, theme.hilitedCornerRadius+1, theme.hilitedCornerRadius+1); +// if (_preeditRange.length == 0) { +// innerBox.origin.y += theme.edgeInset.height; +// innerBox.size.height -= theme.edgeInset.height; +// outerBox.origin.y += theme.edgeInset.height / 2; +// outerBox.size.height -= theme.edgeInset.height / 2; +// } + + if (theme.preeditBackgroundColor != NULL) { + candidatePath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + } - CGFloat halfLinespace = theme.linespace / 2; - if (theme.linear){ + // Drawhighlighted Rect + if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { + if (theme.linear) { NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - [self addGapBetweenLinearCandidates:&leadingRect]; - [self addGapBetweenLinearCandidates:&bodyRect]; - [self addGapBetweenLinearCandidates:&trailingRect]; - NSMutableArray *highlightedPoints; NSMutableArray *highlightedPoints2; // Handles the special case where containing boxes are separated @@ -601,12 +606,12 @@ - (void)drawRect:(NSRect)dirtyRect { highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - innerBox.size.height -= halfLinespace; +// xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); +// xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); +// innerBox.size.height -= halfLinespace; // Expand the boxes to reach proper border - expand(highlightedPoints, innerBox, outerBox); - expand(highlightedPoints2, innerBox, outerBox); +// expand(highlightedPoints, innerBox, outerBox); +// expand(highlightedPoints2, innerBox, outerBox); highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPoints2.count > 0) { highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); @@ -614,21 +619,21 @@ - (void)drawRect:(NSRect)dirtyRect { } else { NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; highlightedRect.size.width = textField.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, - highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } - } +// highlightedRect.size.height += halfLinespace; + highlightedRect.origin.x = textField.origin.x; + highlightedRect.origin.y += theme.edgeInset.height; + // if (_highlightedRange.location+_highlightedRange.length == _text.length) { + // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + // } + // if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + // if (_preeditRange.length == 0) { + // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + // highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + // } else { + // highlightedRect.size.height += theme.hilitedCornerRadius / 2; + // highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + // } + // } NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; expand(highlightedPoints, innerBox, outerBox); highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); @@ -642,20 +647,8 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - NSRect innerBox = preeditRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - innerBox.origin.y += theme.edgeInset.height + 1; - if (_highlightedRange.length == 0) { - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; - } - NSRect outerBox = preeditRect; - outerBox.size.height -= theme.hilitedCornerRadius; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2; + NSRect outerBox = NSInsetRect(preeditRect, theme.edgeInset.width/2, theme.edgeInset.height/2); + NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2 + 1, theme.edgeInset.height/2 + 1); NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; @@ -675,16 +668,17 @@ - (void)drawRect:(NSRect)dirtyRect { } } - [NSBezierPath setDefaultLineWidth:0]; backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); _shape.path = backgroundPath.quartzPath; - // Nothing should extend beyond backgroundPath - borderPath = [backgroundPath copy]; - [borderPath addClip]; + + NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); + borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); borderPath.lineWidth = theme.borderWidth; - -// This block of code enables independent transparencies in highlighted colour and background colour. -// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. + // Nothing should extend beyond backgroundPath + [backgroundPath addClip]; + + // This block of code enables independent transparencies in highlighted colour and background colour. + // Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. #if 0 // Calculate intersections. if (![highlightedPath isEmpty]) { @@ -716,11 +710,14 @@ - (void)drawRect:(NSRect)dirtyRect { [preeditPath setWindingRule:NSEvenOddWindingRule]; #endif - [theme.backgroundColor setFill]; - [backgroundPath fill]; - if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { + if (theme.preeditBackgroundColor && ![pagingPath isEmpty]) { [theme.preeditBackgroundColor setFill]; - [preeditPath fill]; + [backgroundPath fill]; + [theme.backgroundColor setFill]; + [candidatePath fill]; + } else { + [theme.backgroundColor setFill]; + [backgroundPath fill]; } if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { [theme.highlightedStripColor setFill]; @@ -736,13 +733,15 @@ - (void)drawRect:(NSRect)dirtyRect { [highlightedPreeditPath2 fill]; } } - if (theme.borderColor && (theme.borderWidth > 0)) { [theme.borderColor setStroke]; [borderPath stroke]; } - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + + [_textView setTextContainerInset:theme.edgeInset]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + } @end @@ -754,6 +753,7 @@ @implementation SquirrelPanel { NSRange _preeditRange; NSRect _screenRect; CGFloat _maxHeight; + CGFloat _maxTextWidth; NSString *_statusMessage; NSTimer *_statusTimer; @@ -775,61 +775,18 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -CGFloat minimumHeight(NSDictionary *attribute) { - const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; - const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:(NSStringDrawingUsesLineFragmentOrigin)].size.height; - return minimumHeight; -} - -// Use this method to convert charcters to upright position -// Based on the width of the chacter, relative font size matters -void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { - NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; - double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; - // Use the width of the character to determin if they should be upright in vertical writing mode. - // Adjust font base line for better alignment. - const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; - const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; - const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; - const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size; - stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; - NSUInteger i = stringRange.location; - while (i < stringRange.location+stringRange.length) { - NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; - i = range.location + range.length; - NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; - // Also adjust the baseline so upright and lying charcters are properly aligned - if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { - [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; - NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]; - CGFloat widthDiff = charRect.size.width-cjkChar.size.width; - CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; - } else { - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; - } - } -} - -void fixDefaultFont(NSMutableAttributedString *text, BOOL vertical) { +void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); long i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; - NSMutableParagraphStyle *paragraphStyle = [text attribute:NSParagraphStyleAttributeName atIndex:i effectiveRange:¤tFontRange]; if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { - NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; -// CGFloat fontHeight = NSHeight([defaultFont boundingRectForFont]); + NSFontWeight fontWeight = [[charFont.fontDescriptor.fontAttributes objectForKey:NSFontWeightTrait] doubleValue]; + NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize weight:NSFontWeightThin]; [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; -// if ([[text attribute:NSTextLayoutSectionOrientation atIndex:i effectiveRange:¤tFontRange] isEqualToValue:@(NSTextLayoutOrientationVertical)]) - if (vertical) { - [text addAttribute:NSBaselineOffsetAttributeName value:@(-charFont.pointSize/10) range:currentFontRange]; -// paragraphStyle.lineHeightMultiple = 4/3; -// [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:currentFontRange]; - } } - i = currentFontRange.location + currentFontRange.length; + i = NSMaxRange(currentFontRange); } } @@ -853,32 +810,40 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { attrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *highlightedAttrs = [[NSMutableDictionary alloc] init]; - highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedControlTextColor]; + highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedMenuItemTextColor]; highlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; - labelAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize - weight:NSFontWeightRegular]; + labelAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; + labelAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; + NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; - labelHighlightedAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize - weight:NSFontWeightRegular]; + labelHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + labelHighlightedAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; commentAttrs[NSForegroundColorAttributeName] = secondaryTextColor; commentAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; - NSMutableDictionary *commentHighlightedAttrs = [commentAttrs mutableCopy]; + NSMutableDictionary *commentHighlightedAttrs = [[NSMutableDictionary alloc] init]; + commentHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + commentHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; - preeditAttrs[NSForegroundColorAttributeName] = secondaryTextColor; + preeditAttrs[NSForegroundColorAttributeName] = [NSColor textColor]; preeditAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *preeditHighlightedAttrs = [[NSMutableDictionary alloc] init]; - preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; + preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedTextColor]; preeditHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; + pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; + pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize/1.5]; + NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; + NSParagraphStyle *pagingParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs labelAttrs:labelAttrs @@ -887,9 +852,11 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs]; + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs]; [theme setParagraphStyle:paragraphStyle - preeditParagraphStyle:preeditParagraphStyle]; + preeditParagraphStyle:preeditParagraphStyle + pagingParagraphStyle:pagingParagraphStyle]; } - (instancetype)init { @@ -956,17 +923,17 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = _view.text.size.width; + CGFloat textWidth = _view.textView.textStorage.size.width; NSFont *currentFont = theme.attrs[NSFontAttributeName]; CGFloat fontScale = currentFont.pointSize / 12; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - CGFloat maxTextWidth = theme.vertical + _maxTextWidth = theme.vertical ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - if (textWidth > maxTextWidth) { - textWidth = maxTextWidth; + if (textWidth > _maxTextWidth) { + textWidth = _maxTextWidth; } - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); + _view.textView.textContainer.containerSize = NSMakeSize(textWidth, 0); NSRect windowRect; // in vertical mode, the width and height are interchanged @@ -977,7 +944,7 @@ - (void)show { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); + _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, 0); } } @@ -996,7 +963,7 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_preeditRange].size; - windowRect.origin.x += preeditSize.height + theme.edgeInset.width; + windowRect.origin.x += preeditSize.height + theme.edgeInset.width / 2; } } else { windowRect.size = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, @@ -1010,7 +977,7 @@ - (void)show { } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position), + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width / 2, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } @@ -1019,10 +986,10 @@ - (void)show { windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(_screenRect)) { - windowRect.origin.x = (sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(_screenRect)) + (theme.vertical ? theme.edgeInset.height : theme.edgeInset.width); + windowRect.origin.x = sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(_screenRect); } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position) + kOffsetHeight) + (theme.vertical ? theme.edgeInset.width : theme.edgeInset.height); + windowRect.origin.y = (sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position) + kOffsetHeight); } if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); @@ -1069,7 +1036,9 @@ - (void)showPreedit:(NSString *)preedit candidates:(NSArray *)candidates comments:(NSArray *)comments labels:(NSArray *)labels - highlighted:(NSUInteger)index { + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { NSUInteger numCandidates = candidates.count; if (numCandidates || (preedit && preedit.length)) { _statusMessage = nil; @@ -1089,45 +1058,38 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - CGFloat maxTextWidth = theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; _preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); // preedit if (preedit) { - NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; + NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { - NSUInteger highlightedPreeditStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping - attributes:theme.preeditHighlightedAttrs]]; - highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, line.length - highlightedPreeditStart); + NSUInteger highlightedPreeditStart = preeditLine.length; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping + attributes:theme.preeditHighlightedAttrs]]; + highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); } - if (selRange.location + selRange.length < preedit.length) { - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringFromIndex:selRange.location + selRange.length].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + if (NSMaxRange(selRange) < preedit.length) { + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringFromIndex:NSMaxRange(selRange)].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } - [text appendAttributedString:line]; + fixDefaultFont(preeditLine); NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; - paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); - [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit - range:NSMakeRange(0, text.length)]; - + [preeditLine addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePreedit + range:NSMakeRange(0, preeditLine.length)]; + + [text appendAttributedString:preeditLine]; _preeditRange = NSMakeRange(0, text.length); if (numCandidates) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" @@ -1136,36 +1098,36 @@ - (void)showPreedit:(NSString *)preedit candidateStartPos = text.length; } - NSRange highlightedRange = NSMakeRange(NSNotFound, 0); // candidates - CGFloat textWidth = 0.0; + CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); +// _view.seperatorWidth = separatorWidth; + CGFloat candidateTextWidth = 0 - separatorWidth; + NSRange highlightedRange = NSMakeRange(NSNotFound, 0); NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; - NSDictionary *labelAttrs = - (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; - NSDictionary *commentAttrs = - (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; + NSDictionary *labelAttrs = (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; + NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { - NSString *labelString; + NSString *prefixLabelString; if (labels.count > 1 && i < labels.count) { NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - labelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... char labelCharacter = [labels[0] characterAtIndex:i]; - labelString = [NSString stringWithFormat:theme.prefixLabelFormat, labelCharacter]; + prefixLabelString = [[NSString stringWithFormat:theme.prefixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } else { // default: 1. 2. 3... NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; + prefixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:labelString + [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString attributes:labelAttrs]]; // get the label size for indent if (!theme.linear) { @@ -1190,82 +1152,92 @@ - (void)showPreedit:(NSString *)preedit } if (theme.suffixLabelFormat != nil) { - NSString *labelString; + NSString *suffixLabelString; if (labels.count > 1 && i < labels.count) { NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - labelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... char labelCharacter = [labels[0] characterAtIndex:i]; - labelString = [NSString stringWithFormat:theme.suffixLabelFormat, labelCharacter]; + suffixLabelString = [[NSString stringWithFormat:theme.suffixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } else { // default: 1. 2. 3... NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; + suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } NSUInteger suffixLabelStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] initWithString:labelString - attributes:labelAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString + attributes:attrs]]; } + fixDefaultFont(line); // determine if the line is too wide and line break is needed, based on screen size. NSString *separtatorString = @"\u2029"; if (theme.linear) { CGFloat lineWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); - CGFloat spaceWidth = [[NSMutableAttributedString alloc] initWithString: @" " attributes:attrs].size.width; - textWidth += lineWidth + spaceWidth; - if (textWidth > maxTextWidth) { + candidateTextWidth += separatorWidth + lineWidth; + if (candidateTextWidth > _maxTextWidth) { separtatorString = @"\u2028"; - textWidth = lineWidth; + candidateTextWidth = lineWidth; } else { separtatorString = @" "; } } - - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString - attributes:attrs]; - _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:attrs]; - NSMutableParagraphStyle *paragraphStyleCandidate; + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (i == 0) { - NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; - firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - paragraphStyleCandidate = firstParagraphStyle; +// paragraphStyleCandidate.paragraphSpacingBefore = theme.linespace / 2; } else { - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - NSUInteger separatorStart = text.length; [text appendAttributedString:separator]; } if (theme.linear) { - paragraphStyleCandidate.lineSpacing = theme.linespace; +// paragraphStyleCandidate.lineSpacing = theme.linespace; } else { paragraphStyleCandidate.headIndent = labelWidth; } - paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs) + theme.linespace; - // Use left-to-right marks to declare the default writing direction and - // prevent strong right-to-left characters from setting the wirint direction - paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; - - [line addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, line.length)]; - + [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; if (i == index) { highlightedRange = NSMakeRange(text.length, line.length); } [text appendAttributedString:line]; } - // Fix font rendering - if (theme.vertical) { - [text addAttribute:NSVerticalGlyphFormAttributeName value:@YES range:NSMakeRange(0, text.length)]; - [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; + // paging indication + NSRange pagingRange = NSMakeRange(NSNotFound, 0); + + if (numCandidates) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; + + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" + attributes:theme.pagingAttrs]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(pageNum ? @"◀" : @"◁") + attributes:theme.pagingAttrs]]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] + attributes:theme.pagingAttrs]]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(lastPage ? @"▷" : @"▶") + attributes:theme.pagingAttrs]]; + + fixDefaultFont(paging); + NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; +// paragraphStylePaging.minimumLineHeight = minimumHeight(theme.pagingAttrs); + [paging addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePaging + range:NSMakeRange(0, paging.length)]; + + pagingRange = NSMakeRange(text.length, paging.length); + [text appendAttributedString:paging]; } - fixDefaultFont(text, theme.vertical); + + [_view.textView.textStorage setAttributedString:text]; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; // text done! - [_view setText:text]; - [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange]; + [self show]; } @@ -1275,14 +1247,14 @@ - (void)updateStatus:(NSString *)message { - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; - if (theme.vertical) { - [text addAttribute:NSVerticalGlyphFormAttributeName value:@YES range:NSMakeRange(0, text.length)]; - [text addAttribute:NSTextLayoutSectionOrientation value:@(NSTextLayoutOrientationVertical) range:NSMakeRange(0, text.length)]; - } - [_view setText:text]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; + + fixDefaultFont(text); + [_view.textView.textStorage setAttributedString:text]; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1394,8 +1366,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; - CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; - CGFloat spacing = [config getDouble:@"style/spacing"]; + CGFloat lineSpacing = fmax([config getDouble:@"style/line_spacing"], hilitedCornerRadius); + CGFloat spacing = fmax([config getDouble:@"style/spacing"], hilitedCornerRadius); CGFloat baseOffset = [config getDouble:@"style/base_offset"]; NSColor *backgroundColor; @@ -1596,22 +1568,24 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } NSFontDescriptor *labelFontDescriptor = nil; NSFont *labelFont = nil; + NSMutableDictionary *labelFontAttr; + labelFontAttr[NSFontFixedAdvanceAttribute] = @(labelFontSize); + labelFontAttr[NSFontSizeAttribute] = @(labelFontSize); if (labelFontName != nil) { labelFontDescriptor = getFontDescriptor(labelFontName); if (labelFontDescriptor == nil) { - labelFontDescriptor = fontDescriptor; + labelFontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr]; } if (labelFontDescriptor != nil) { labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; } } if (labelFont == nil) { -// if (fontDescriptor != nil) { -// labelFont = [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize]; -// } else { -// labelFont = [NSFont fontWithName:font.fontName size:labelFontSize]; -// } - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + if (fontDescriptor != nil) { + labelFont = [NSFont fontWithDescriptor:[fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr] size:labelFontSize]; + } else { + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + } } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1631,24 +1605,27 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; } } + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize/1.5]; + + CGFloat lineHeight = font.ascender - font.descender; + NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + paragraphStyle.alignment = NSTextAlignmentJustified; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2; + paragraphStyle.lineSpacing = lineSpacing/2; - // CGFloat lineHeight = fmax(fmax(fontSize, labelFontSize), commentFontSize) * 1.3; + NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + preeditParagraphStyle.alignment = NSTextAlignmentLeft; + preeditParagraphStyle.minimumLineHeight = lineHeight; + preeditParagraphStyle.paragraphSpacing = spacing; - NSMutableParagraphStyle *paragraphStyle = - [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - // paragraphStyle.minimumLineHeight = lineHeight + lineSpacing; - // paragraphStyle.maximumLineHeight = lineHeight + lineSpacing; - paragraphStyle.paragraphSpacing = lineSpacing / 2; - paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; - // paragraphStyle.lineSpacing = lineSpacing; -// paragraphStyle.defaultTabInterval = fontSize * 1.2; + NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + pagingParagraphStyle.alignment = NSTextAlignmentRight; - NSMutableParagraphStyle *preeditParagraphStyle = - [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - // preeditParagraphStyle.minimumLineHeight = fontSize * 1.3 + spacing; - // preeditParagraphStyle.maximumLineHeight = fontSize * 1.3 + spacing; - preeditParagraphStyle.paragraphSpacing = spacing / 2 + hilitedCornerRadius / 2; - // preeditParagraphStyle.lineSpacing = spacing; + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left + // characters from setting the writing direction in case the label are direction-less symbols + paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -1658,6 +1635,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSMutableDictionary *commentHighlightedAttrs = [theme.commentHighlightedAttrs mutableCopy]; NSMutableDictionary *preeditAttrs = [theme.preeditAttrs mutableCopy]; NSMutableDictionary *preeditHighlightedAttrs = [theme.preeditHighlightedAttrs mutableCopy]; + NSMutableDictionary *pagingAttrs = [theme.pagingAttrs mutableCopy]; attrs[NSFontAttributeName] = font; highlightedAttrs[NSFontAttributeName] = font; @@ -1667,6 +1645,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSFontAttributeName] = commentFont; preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; + pagingAttrs[NSFontAttributeName] = pagingFont; attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); @@ -1675,21 +1654,26 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSSuperscriptAttributeName] = @(-1); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; - backgroundColor = backgroundColor ? backgroundColor : [NSColor windowBackgroundColor]; + backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; + borderColor = borderColor ? borderColor : [NSColor gridColor]; + preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : [NSColor windowBackgroundColor]; candidateTextColor = candidateTextColor ? candidateTextColor : [NSColor controlTextColor]; + highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedMenuItemTextColor]; + highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : [NSColor selectedContentBackgroundColor]; candidateLabelColor = candidateLabelColor ? candidateLabelColor : - isNative ? secondaryTextColor : blendColors(candidateTextColor, backgroundColor); - highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedControlTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : [NSColor selectedTextBackgroundColor]; + isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : - isNative ? secondaryTextColor : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); + isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); commentTextColor = commentTextColor ? commentTextColor : secondaryTextColor; - highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : commentTextColor; - textColor = textColor ? textColor : secondaryTextColor; - highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; + highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : [NSColor alternateSelectedControlTextColor]; + textColor = textColor ? textColor : [NSColor textColor]; + highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor selectedTextColor]; + highlightedBackColor = highlightedBackColor ? highlightedBackColor : [NSColor selectedTextBackgroundColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; @@ -1699,6 +1683,17 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; preeditAttrs[NSForegroundColorAttributeName] = textColor; preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; + pagingAttrs[NSForegroundColorAttributeName] = candidateLabelColor; + + attrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + labelAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + highlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + preeditAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; [theme setAttrs:attrs labelAttrs:labelAttrs @@ -1707,10 +1702,12 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs]; + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs]; [theme setParagraphStyle:paragraphStyle - preeditParagraphStyle:preeditParagraphStyle]; + preeditParagraphStyle:preeditParagraphStyle + pagingParagraphStyle:pagingParagraphStyle]; [theme setBackgroundColor:backgroundColor highlightedStripColor:highlightedCandidateBackColor @@ -1718,17 +1715,14 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - NSSize edgeInset; - if (vertical) { - edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); - } else { - edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); - } + borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)); + borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)); + NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius edgeInset:edgeInset - borderWidth:MIN(borderHeight, borderWidth) + borderWidth:MAX(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) @@ -1736,9 +1730,10 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo linear:linear vertical:vertical inlinePreedit:inlinePreedit - inlineCandidate:inlineCandidate]; + inlineCandidate:inlineCandidate]; theme.native = isNative; theme.candidateFormat = (candidateFormat ? candidateFormat : kDefaultCandidateFormat); } + @end From c281ad6305f40bafd0e89c15138ceadf737a9cdb Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 18 Apr 2023 15:00:05 +0200 Subject: [PATCH 042/164] plugin --- INSTALL.md | 2 +- librime | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f116f726b..a2c55e83d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram # rime/librime-charcode rime/librime-legacy hchunhui/librime-lua lotem/librime-proto ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... ``` ### Shortcut: get the latest librime release diff --git a/librime b/librime index b239cb465..aa6c46bee 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit b239cb465a70970110b5179e147dd3d685b3a75e +Subproject commit aa6c46bee76d919d068cdf0974c27218c669974e From 962de06dc489d1898c9643a4351e80ee9e9c8d5b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 21 Apr 2023 07:56:58 +0200 Subject: [PATCH 043/164] UI --- Squirrel.xcodeproj/project.pbxproj | 20 ----- SquirrelApplicationDelegate.m | 26 ++++--- SquirrelConfig.h | 2 +- SquirrelConfig.m | 2 +- SquirrelInputController.m | 25 ++++--- SquirrelPanel.h | 2 +- SquirrelPanel.m | 113 ++++++++++++++++++----------- 7 files changed, 101 insertions(+), 89 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 78cc8ff28..e93df2784 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -35,7 +35,6 @@ 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 44CD7D9E1828D981006E9222 /* rime.pdf */; }; 44E21A9016A653E700C2B08F /* rime_deployer in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8E16A653E700C2B08F /* rime_deployer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E21A9116A653E700C2B08F /* rime_dict_manager in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8F16A653E700C2B08F /* rime_dict_manager */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44E98EC214AE1AC900847AD6 /* utf8.cpp */; }; 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 44F7708E152B3334005CF491 /* dsa_pub.pem */; }; 44F84AD714E94C490005D70B /* SquirrelPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 44F84AD614E94C490005D70B /* SquirrelPanel.m */; }; 77AA68142588916F00A592E2 /* hk2s.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67E22588916300A592E2 /* hk2s.json */; }; @@ -227,11 +226,6 @@ 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; 44E21A8F16A653E700C2B08F /* rime_dict_manager */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_dict_manager; path = bin/rime_dict_manager; sourceTree = ""; }; - 44E98EA514AE16DD00847AD6 /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; - 44E98EA614AE16DD00847AD6 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; - 44E98EA714AE16DD00847AD6 /* unchecked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unchecked.h; sourceTree = ""; }; - 44E98EC114AE1AC900847AD6 /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = ""; }; - 44E98EC214AE1AC900847AD6 /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; 44F1EB381431F8270015FD04 /* Squirrel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Squirrel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44F7708E152B3334005CF491 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; 44F84AD514E94C490005D70B /* SquirrelPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SquirrelPanel.h; sourceTree = ""; }; @@ -308,7 +302,6 @@ 080E96DDFE201D6D7F000001 /* Sources */ = { isa = PBXGroup; children = ( - 44E98EA414AE16DD00847AD6 /* utf8 */, 44AC95161430CF6000C888FB /* SquirrelApplicationDelegate.h */, 44AC95171430CF6000C888FB /* SquirrelApplicationDelegate.m */, 44AC95181430CF6000C888FB /* SquirrelInputController.h */, @@ -318,8 +311,6 @@ 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, 4443A8391828CC5100731305 /* input_source.m */, 29B97316FDCFA39411CA2CEA /* main.m */, - 44E98EC114AE1AC900847AD6 /* utf8.h */, - 44E98EC214AE1AC900847AD6 /* utf8.cpp */, 44F84AD514E94C490005D70B /* SquirrelPanel.h */, 44F84AD614E94C490005D70B /* SquirrelPanel.m */, 7BDB21211C6EF1BE0025E351 /* SquirrelConfig.h */, @@ -454,16 +445,6 @@ name = SharedSupport; sourceTree = ""; }; - 44E98EA414AE16DD00847AD6 /* utf8 */ = { - isa = PBXGroup; - children = ( - 44E98EA514AE16DD00847AD6 /* checked.h */, - 44E98EA614AE16DD00847AD6 /* core.h */, - 44E98EA714AE16DD00847AD6 /* unchecked.h */, - ); - path = utf8; - sourceTree = ""; - }; 7B5488A71D2DAC9D0056A1BE /* plum */ = { isa = PBXGroup; children = ( @@ -579,7 +560,6 @@ A47C48DF105E8CE8006D528B /* macos_keycode.m in Sources */, 44AC951A1430CF6000C888FB /* SquirrelApplicationDelegate.m in Sources */, 44AC951B1430CF6000C888FB /* SquirrelInputController.m in Sources */, - 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */, 4443A83A1828CC5100731305 /* input_source.m in Sources */, 44F84AD714E94C490005D70B /* SquirrelPanel.m in Sources */, ); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 09f574d9a..db1479ab6 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -24,7 +24,7 @@ -(IBAction)syncUserData:(id)sender -(IBAction)configure:(id)sender { - [[NSWorkspace sharedWorkspace] openFile:(@"~/Library/Rime").stringByStandardizingPath]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[@"file://" stringByAppendingString:(@"~/Library/Rime").stringByStandardizingPath]]]; } -(IBAction)openWiki:(id)sender @@ -47,11 +47,12 @@ void show_message(const char* msg_text, const char* msg_id) { } } -static void show_status_message(const char* msg_text, const char* msg_id) { +static void show_status_message(const char *msg_text_long, const char *msg_text_short, const char* msg_id) { SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; if (panel) { - NSString *message = @(msg_text); - [panel updateStatus: message]; + NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; + NSString *msgShort = msg_text_short ? @(msg_text_short) : nil; + [panel updateStatusLong: msgLong statusShort: msgShort]; } } @@ -79,7 +80,7 @@ void notification_handler(void* context_object, RimeSessionId session_id, const char* schema_name = strchr(message_value, '/'); if (schema_name) { ++schema_name; - show_status_message(schema_name, message_type); + show_status_message(schema_name, schema_name, message_type); } return; } @@ -87,9 +88,12 @@ void notification_handler(void* context_object, RimeSessionId session_id, if (!strcmp(message_type, "option")) { Bool state = message_value[0] != '!'; const char* option_name = message_value + !state; - const char *state_label = rime_get_api()->get_state_label(session_id, option_name, state); - if (state_label) { - show_status_message(state_label, message_type); + struct rime_string_slice_t state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); + struct rime_string_slice_t state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); + + if (state_label_long.str || state_label_short.str) { + const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; + show_status_message(state_label_long.str, short_message, message_type); } } } @@ -136,7 +140,7 @@ - (void)shutdownRime { -(void)loadSettings { _config = [[SquirrelConfig alloc] init]; - if (!_config.openBaseConfig) { + if (![_config openBaseConfig]) { return; } @@ -181,13 +185,13 @@ -(BOOL)problematicLaunchDetected options:NSDataReadingUncached error:nil]; if (archive) { - NSDate* previousLaunch = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; + NSDate* previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate* now = [NSDate date]; - NSData* record = [NSKeyedArchiver archivedDataWithRootObject:now]; + NSData* record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; [record writeToFile:logfile atomically:NO]; return detected; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index b91a384d8..c4bd50145 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -9,7 +9,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; @property(nonatomic, copy) NSString *colorSpace; @property(nonatomic, readonly) NSString *schemaId; -@property (NS_NONATOMIC_IOSONLY, readonly) BOOL openBaseConfig; +- (BOOL)openBaseConfig; - (BOOL)openWithSchemaId:(NSString *)schemaId baseConfig:(SquirrelConfig *)config; - (void)close; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 698f350e7..d816f1b40 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -155,7 +155,7 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { #pragma mark - Private methods - (id)cachedValueOfClass:(Class)aClass forKey:(NSString *)key { - id value = _cache[key]; + id value = [_cache objectForKey:key]; if (value && [value isKindOfClass:aClass]) { return value; } diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 3243e27d1..cfcb3b134 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -4,7 +4,6 @@ #import "SquirrelConfig.h" #import "SquirrelPanel.h" #import "macos_keycode.h" -#import "utf8.h" #import #import @@ -168,12 +167,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender [self rimeUpdate]; } } break; -// case NSEventTypeLeftMouseDown: { -// if (_preeditString.length) { -// [_currentClient makeFirstResponder:nil]; -// [self commitComposition:_currentClient]; -// } -// } break; default: break; } @@ -533,6 +526,13 @@ -(void)rimeConsumeCommittedText } } +NSString *substr(const char *str, int length) { + char substring[length+1]; + strncpy(substring, str, length); + substring[length] = '\0'; + return [NSString stringWithCString:substring encoding:NSUTF8StringEncoding]; +} + -(void)rimeUpdate { //NSLog(@"rimeUpdate"); @@ -548,7 +548,8 @@ -(void)rimeUpdate _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || rime_get_api()->get_option(_session, "inline"); - _inlineCandidate = NSApp.squirrelAppDelegate.panel.inlineCandidate; + _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && + !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); } @@ -561,9 +562,9 @@ -(void)rimeUpdate const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; - NSUInteger start = utf8len(preedit, ctx.composition.sel_start); - NSUInteger end = utf8len(preedit, ctx.composition.sel_end); - NSUInteger caretPos = utf8len(preedit, ctx.composition.cursor_pos); + NSUInteger start = substr(preedit, ctx.composition.sel_start).length; + NSUInteger end = substr(preedit, ctx.composition.sel_end).length; + NSUInteger caretPos = substr(preedit, ctx.composition.cursor_pos).length; NSRange selRange = NSMakeRange(start, end - start); if (_inlineCandidate) { const char *candidatePreview = ctx.commit_text_preview; @@ -589,7 +590,7 @@ -(void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @"\u3000" : @"") selRange:empty caretPos:0]; + [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; } } // update candidates diff --git a/SquirrelPanel.h b/SquirrelPanel.h index dcabd468e..1b01aa9b2 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -28,7 +28,7 @@ -(void)hide; --(void)updateStatus:(NSString*)message; +-(void)updateStatusLong:(NSString*)messageLong statusShort:(NSString*)messageShort; -(void)loadConfig:(SquirrelConfig*)config forDarkMode:(BOOL)isDark; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 20fbf37f1..265b813e6 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -91,9 +91,12 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; -@property(nonatomic, strong, readonly) NSString *prefixLabelFormat, *suffixLabelFormat; +@property(nonatomic, strong, readonly) NSString *prefixLabelFormat; +@property(nonatomic, strong, readonly) NSString *suffixLabelFormat; +@property(nonatomic, strong, readonly) NSString *statusMessageType; - (void)setCandidateFormat:(NSString *)candidateFormat; +- (void)setStatusMessageType:(NSString *)statusMessageType; - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedStripColor:(NSColor *)highlightedStripColor @@ -134,7 +137,7 @@ @implementation SquirrelTheme - (void)setCandidateFormat:(NSString *)candidateFormat { // in the candiate format, everything other than '%@' is considered part of the label - NSRange candidateRange = [candidateFormat rangeOfString:@"%@" options:NSLiteralSearch]; + NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; if (candidateRange.location == NSNotFound) { _prefixLabelFormat = candidateFormat; _suffixLabelFormat = nil; @@ -158,6 +161,14 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } } +- (void)setStatusMessageType:(NSString *)type { + if ([type isEqualToString: @"long"] || [type isEqualToString: @"short"] || [type isEqualToString: @"mix"]) { + _statusMessageType = type; + } else { + _statusMessageType = @"mix"; + } +} + - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor @@ -176,7 +187,7 @@ - (void)setCornerRadius:(double)cornerRadius borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(CGFloat)alpha + alpha:(double)alpha translucency:(BOOL)translucency linear:(BOOL)linear vertical:(BOOL)vertical @@ -236,12 +247,11 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRect contentRect; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; -@property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; -- (void) drawViewWith:(NSRange)hilightedRange +- (void) drawViewWith:(NSRange)highlightedRange preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange; @@ -282,10 +292,10 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.layer.masksToBounds = YES; } _textView = [[NSTextView alloc] initWithFrame:frameRect]; - // Use textStorage to store text and manage all text layout and draws NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; _textView.drawsBackground = NO; + _textView.editable = NO; _textView.selectable = NO; [_textView replaceTextContainer:textContainer]; _textView.layoutManager.backgroundLayoutEnabled = YES; @@ -295,7 +305,6 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; -// _textView.usesAdaptiveColorMappingForDarkAppearance = YES; } return self; } @@ -315,11 +324,11 @@ - (NSRect)contentRectForRange:(NSRange)range { } // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWith:(NSRange)hilightedRange +- (void) drawViewWith:(NSRange)highlightedRange preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange { - _highlightedRange = hilightedRange; + _highlightedRange = highlightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; @@ -532,7 +541,7 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *pagingPath; NSBezierPath *candidatePath; SquirrelTheme * theme = self.currentTheme; - + [NSBezierPath setDefaultLineWidth:0]; NSRect backgroundRect = dirtyRect; NSRect textField = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); @@ -542,10 +551,10 @@ - (void)drawRect:(NSRect)dirtyRect { if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; preeditRect.size.width = backgroundRect.size.width; - preeditRect.size.height += theme.edgeInset.height/2 + theme.preeditLinespace; + preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace; preeditRect.origin = backgroundRect.origin; if (_highlightedRange.length == 0) { - preeditRect.size.height += theme.edgeInset.height*3/2 - theme.preeditLinespace; + preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace; } if (theme.preeditBackgroundColor != nil) { preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); @@ -557,9 +566,9 @@ - (void)drawRect:(NSRect)dirtyRect { if (_pagingRange.length > 0) { pagingRect = [self contentRectForRange:_pagingRange]; pagingRect.size.width = backgroundRect.size.width; - pagingRect.size.height += theme.edgeInset.height/2; + pagingRect.size.height += theme.edgeInset.height; pagingRect.origin.x = backgroundRect.origin.x; - pagingRect.origin.y += theme.edgeInset.height*3/2; + pagingRect.origin.y += theme.edgeInset.height; if (theme.preeditBackgroundColor != nil) { pagingPath = drawSmoothLines(rectVertex(pagingRect), 0, 0); } @@ -575,15 +584,9 @@ - (void)drawRect:(NSRect)dirtyRect { candidateRect.origin.y += theme.edgeInset.height/2; } - NSRect outerBox = NSInsetRect(candidateRect, theme.edgeInset.width, theme.edgeInset.height/2); - NSRect innerBox = NSInsetRect(outerBox, theme.hilitedCornerRadius+1, theme.hilitedCornerRadius+1); -// if (_preeditRange.length == 0) { -// innerBox.origin.y += theme.edgeInset.height; -// innerBox.size.height -= theme.edgeInset.height; -// outerBox.origin.y += theme.edgeInset.height / 2; -// outerBox.size.height -= theme.edgeInset.height / 2; -// } - + NSRect outerBox = NSInsetRect(candidateRect, theme.edgeInset.width/2, 0); + NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2+1, halfLinespace+1); + if (theme.preeditBackgroundColor != NULL) { candidatePath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } @@ -647,8 +650,16 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - NSRect outerBox = NSInsetRect(preeditRect, theme.edgeInset.width/2, theme.edgeInset.height/2); - NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2 + 1, theme.edgeInset.height/2 + 1); + NSRect outerBox = preeditRect; + outerBox.size.width -= theme.edgeInset.width; + outerBox.size.height -= theme.edgeInset.height/2 + theme.hilitedCornerRadius/2; + outerBox.origin.x += theme.edgeInset.width/2; + outerBox.origin.y += theme.edgeInset.height/2; + NSRect innerBox = preeditRect; + innerBox.size.width -= theme.edgeInset.width * 2 + 2; + innerBox.size.height -= theme.edgeInset.height * 2 + theme.preeditLinespace; + innerBox.origin.x += theme.edgeInset.width + 1; + innerBox.origin.y += theme.edgeInset.height + 1; NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; @@ -668,15 +679,15 @@ - (void)drawRect:(NSRect)dirtyRect { } } - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); _shape.path = backgroundPath.quartzPath; - + NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); borderPath.lineWidth = theme.borderWidth; // Nothing should extend beyond backgroundPath [backgroundPath addClip]; - + // This block of code enables independent transparencies in highlighted colour and background colour. // Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. #if 0 @@ -710,11 +721,16 @@ - (void)drawRect:(NSRect)dirtyRect { [preeditPath setWindingRule:NSEvenOddWindingRule]; #endif - if (theme.preeditBackgroundColor && ![pagingPath isEmpty]) { + if (theme.preeditBackgroundColor && + (_preeditRange.length > 0 || _highlightedRange.length > 0)) { [theme.preeditBackgroundColor setFill]; [backgroundPath fill]; - [theme.backgroundColor setFill]; - [candidatePath fill]; + if (_highlightedRange.length > 0){ + [[NSColor clearColor] setFill]; + [candidatePath fill]; + [theme.backgroundColor setFill]; + [candidatePath fill]; + } } else { [theme.backgroundColor setFill]; [backgroundPath fill]; @@ -738,7 +754,6 @@ - (void)drawRect:(NSRect)dirtyRect { [borderPath stroke]; } - [_textView setTextContainerInset:theme.edgeInset]; NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; @@ -1100,7 +1115,6 @@ - (void)showPreedit:(NSString *)preedit // candidates CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); -// _view.seperatorWidth = separatorWidth; CGFloat candidateTextWidth = 0 - separatorWidth; NSRange highlightedRange = NSMakeRange(NSNotFound, 0); NSUInteger i; @@ -1186,14 +1200,10 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:attrs]; NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - if (i == 0) { -// paragraphStyleCandidate.paragraphSpacingBefore = theme.linespace / 2; - } else { + if (i > 0) { [text appendAttributedString:separator]; } - if (theme.linear) { -// paragraphStyleCandidate.lineSpacing = theme.linespace; - } else { + if (!theme.linear) { paragraphStyleCandidate.headIndent = labelWidth; } [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; @@ -1223,7 +1233,6 @@ - (void)showPreedit:(NSString *)preedit fixDefaultFont(paging); NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; -// paragraphStylePaging.minimumLineHeight = minimumHeight(theme.pagingAttrs); [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; @@ -1241,15 +1250,30 @@ - (void)showPreedit:(NSString *)preedit [self show]; } -- (void)updateStatus:(NSString *)message { - _statusMessage = message; +- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { + SquirrelTheme *theme = _view.currentTheme; + if ([theme.statusMessageType isEqualToString:@"mix"]) { + if (messageShort) { + _statusMessage = messageShort; + } else { + _statusMessage = messageLong; + } + } else if ([theme.statusMessageType isEqualToString:@"long"]) { + _statusMessage = messageLong; + } else if ([theme.statusMessageType isEqualToString:@"short"]) { + if (messageShort) { + _statusMessage = messageShort; + } else if (messageLong) { + _statusMessage = [messageLong substringWithRange:[messageLong rangeOfComposedCharacterSequenceAtIndex:0]]; + } + } } - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; - fixDefaultFont(text); + [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1353,6 +1377,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; + NSString *statusMessageType = [config getString:@"style/status_message_type"]; NSString *candidateFormat = [config getString:@"style/candidate_format"]; NSString *fontName = [config getString:@"style/font_face"]; @@ -1695,6 +1720,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + [theme setStatusMessageType:statusMessageType]; + [theme setAttrs:attrs labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs From fb02e26eee4988a4503b0dcdfd9a04e88f69020a Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 30 Mar 2023 15:10:15 +0200 Subject: [PATCH 044/164] Support Clear --- macos_keycode.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macos_keycode.m b/macos_keycode.m index a4437a324..c59b56981 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -91,7 +91,7 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) {OSX_VK_KEYPAD_7, XK_KP_7}, {OSX_VK_KEYPAD_8, XK_KP_8}, {OSX_VK_KEYPAD_9, XK_KP_9}, - //OSX_VK_KEYPAD_CLEAR -> ? + {OSX_VK_KEYPAD_CLEAR, XK_Clear}, {OSX_VK_KEYPAD_COMMA, XK_KP_Separator}, {OSX_VK_KEYPAD_DOT, XK_KP_Decimal}, {OSX_VK_KEYPAD_EQUAL, XK_KP_Equal}, From c6f11b2d7a7d4a80187836392e9abd914d144891 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 5 Apr 2023 15:45:16 +0200 Subject: [PATCH 045/164] Misc --- INSTALL.md | 2 +- SquirrelInputController.m | 7 ++++--- librime | 2 +- macos_keycode.h | 8 -------- macos_keycode.m | 24 ++++++++++++------------ utf8.cpp | 6 ++++-- 6 files changed, 22 insertions(+), 27 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f116f726b..a2c55e83d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram # rime/librime-charcode rime/librime-legacy hchunhui/librime-lua lotem/librime-proto ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... ``` ### Shortcut: get the latest librime release diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 657291a24..a89d1c60c 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -138,6 +138,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; + _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -146,7 +147,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender int keyCode = event.keyCode; NSString* keyChars = event.charactersIgnoringModifiers; - if (!isalpha(keyChars.UTF8String[0])) { + if ((modifiers & OSX_SHIFT_MASK) && + !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) { keyChars = event.characters; } //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", @@ -154,7 +156,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender // translate osx keyevents to rime keyevents int rime_keycode = osx_keycode_to_rime_keycode(keyCode, keyChars.UTF8String[0], - modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); + modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) @@ -177,7 +179,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } } - _lastModifier = modifiers; _lastEventType = event.type; return handled; diff --git a/librime b/librime index 62333d727..aa6c46bee 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 62333d727ed74a095023aba77f6fcb5f96b4f514 +Subproject commit aa6c46bee76d919d068cdf0974c27218c669974e diff --git a/macos_keycode.h b/macos_keycode.h index db17ceaac..e24e44375 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -3,14 +3,6 @@ #define _MACOS_KEYCODE_H_ // masks -#define OSX_LEFT_CTRL_MASK 1 << 0 -#define OSX_LEFT_SHIFT_MASK 1 << 1 -#define OSX_RIGHT_SHIFT_MASK 1 << 2 -#define OSX_LEFT_COMMAND_MASK 1 << 3 -#define OSX_RIGHT_COMMAND_MASK 1 << 4 -#define OSX_LEFT_ALT_MASK 1 << 5 -#define OSX_RIGHT_ALT_MASK 1 << 6 -#define OSX_RIGHT_CTRL_MASK 1 << 13 #define OSX_CAPITAL_MASK 1 << 16 #define OSX_SHIFT_MASK 1 << 17 #define OSX_CTRL_MASK 1 << 18 diff --git a/macos_keycode.m b/macos_keycode.m index c59b56981..780a5be56 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -131,18 +131,18 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) if (keychar >= 0x20 && keychar <= 0x7e) { return keychar; } - else if (keychar == 0x1b) { // ^[ - return XK_bracketleft; - } - else if (keychar == 0x1c) { // ^\ - return XK_backslash; - } - else if (keychar == 0x1d) { // ^] - return XK_bracketright; - } - else if (keychar == 0x1f) { // ^_ - return XK_minus; - } +// else if (keychar == 0x1b) { // ^[ +// return XK_bracketleft; +// } +// else if (keychar == 0x1c) { // ^\ +// return XK_backslash; +// } +// else if (keychar == 0x1d) { // ^] +// return XK_bracketright; +// } +// else if (keychar == 0x1f) { // ^_ +// return XK_minus; +// } return XK_VoidSymbol; } diff --git a/utf8.cpp b/utf8.cpp index 24c6590b1..786e04b96 100644 --- a/utf8.cpp +++ b/utf8.cpp @@ -1,7 +1,9 @@ #include "utf8.h" #include "utf8/unchecked.h" - +#include unsigned long utf8len(const char *text, unsigned octet_len) { - return utf8::unchecked::distance(text, text + octet_len); + std::vector utf16result; + utf8::unchecked::utf8to16(text, text + octet_len, std::back_inserter(utf16result)); + return utf16result.size(); } From 0978d6c93e64253466a8ed8b58da4be7315985b2 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 21 Apr 2023 17:04:48 +0200 Subject: [PATCH 046/164] Merge branch 'master' of https://github.com/rime/squirrel --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 8911db66b..3743c0a59 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 8911db66b0e5d43c2c3e47ffec06bfd10201daae +Subproject commit 3743c0a592308ca7cff15b2b565e274809f671c7 From bb6edddb8e9bf52d9274bca78fe60c893dada5ba Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 28 Mar 2023 01:12:14 +0200 Subject: [PATCH 047/164] UI --- Squirrel.xcodeproj/project.pbxproj | 20 - SquirrelApplicationDelegate.m | 26 +- SquirrelConfig.h | 2 +- SquirrelConfig.m | 2 +- SquirrelInputController.m | 37 +- SquirrelPanel.h | 6 +- SquirrelPanel.m | 791 +++++++++++++++-------------- 7 files changed, 455 insertions(+), 429 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 78cc8ff28..e93df2784 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -35,7 +35,6 @@ 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 44CD7D9E1828D981006E9222 /* rime.pdf */; }; 44E21A9016A653E700C2B08F /* rime_deployer in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8E16A653E700C2B08F /* rime_deployer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E21A9116A653E700C2B08F /* rime_dict_manager in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8F16A653E700C2B08F /* rime_dict_manager */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44E98EC214AE1AC900847AD6 /* utf8.cpp */; }; 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 44F7708E152B3334005CF491 /* dsa_pub.pem */; }; 44F84AD714E94C490005D70B /* SquirrelPanel.m in Sources */ = {isa = PBXBuildFile; fileRef = 44F84AD614E94C490005D70B /* SquirrelPanel.m */; }; 77AA68142588916F00A592E2 /* hk2s.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67E22588916300A592E2 /* hk2s.json */; }; @@ -227,11 +226,6 @@ 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; 44E21A8F16A653E700C2B08F /* rime_dict_manager */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_dict_manager; path = bin/rime_dict_manager; sourceTree = ""; }; - 44E98EA514AE16DD00847AD6 /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; - 44E98EA614AE16DD00847AD6 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; - 44E98EA714AE16DD00847AD6 /* unchecked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unchecked.h; sourceTree = ""; }; - 44E98EC114AE1AC900847AD6 /* utf8.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utf8.h; sourceTree = ""; }; - 44E98EC214AE1AC900847AD6 /* utf8.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = utf8.cpp; sourceTree = ""; }; 44F1EB381431F8270015FD04 /* Squirrel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Squirrel.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44F7708E152B3334005CF491 /* dsa_pub.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = dsa_pub.pem; sourceTree = ""; }; 44F84AD514E94C490005D70B /* SquirrelPanel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SquirrelPanel.h; sourceTree = ""; }; @@ -308,7 +302,6 @@ 080E96DDFE201D6D7F000001 /* Sources */ = { isa = PBXGroup; children = ( - 44E98EA414AE16DD00847AD6 /* utf8 */, 44AC95161430CF6000C888FB /* SquirrelApplicationDelegate.h */, 44AC95171430CF6000C888FB /* SquirrelApplicationDelegate.m */, 44AC95181430CF6000C888FB /* SquirrelInputController.h */, @@ -318,8 +311,6 @@ 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, 4443A8391828CC5100731305 /* input_source.m */, 29B97316FDCFA39411CA2CEA /* main.m */, - 44E98EC114AE1AC900847AD6 /* utf8.h */, - 44E98EC214AE1AC900847AD6 /* utf8.cpp */, 44F84AD514E94C490005D70B /* SquirrelPanel.h */, 44F84AD614E94C490005D70B /* SquirrelPanel.m */, 7BDB21211C6EF1BE0025E351 /* SquirrelConfig.h */, @@ -454,16 +445,6 @@ name = SharedSupport; sourceTree = ""; }; - 44E98EA414AE16DD00847AD6 /* utf8 */ = { - isa = PBXGroup; - children = ( - 44E98EA514AE16DD00847AD6 /* checked.h */, - 44E98EA614AE16DD00847AD6 /* core.h */, - 44E98EA714AE16DD00847AD6 /* unchecked.h */, - ); - path = utf8; - sourceTree = ""; - }; 7B5488A71D2DAC9D0056A1BE /* plum */ = { isa = PBXGroup; children = ( @@ -579,7 +560,6 @@ A47C48DF105E8CE8006D528B /* macos_keycode.m in Sources */, 44AC951A1430CF6000C888FB /* SquirrelApplicationDelegate.m in Sources */, 44AC951B1430CF6000C888FB /* SquirrelInputController.m in Sources */, - 44E98EC314AE1AC900847AD6 /* utf8.cpp in Sources */, 4443A83A1828CC5100731305 /* input_source.m in Sources */, 44F84AD714E94C490005D70B /* SquirrelPanel.m in Sources */, ); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 09f574d9a..db1479ab6 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -24,7 +24,7 @@ -(IBAction)syncUserData:(id)sender -(IBAction)configure:(id)sender { - [[NSWorkspace sharedWorkspace] openFile:(@"~/Library/Rime").stringByStandardizingPath]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[@"file://" stringByAppendingString:(@"~/Library/Rime").stringByStandardizingPath]]]; } -(IBAction)openWiki:(id)sender @@ -47,11 +47,12 @@ void show_message(const char* msg_text, const char* msg_id) { } } -static void show_status_message(const char* msg_text, const char* msg_id) { +static void show_status_message(const char *msg_text_long, const char *msg_text_short, const char* msg_id) { SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; if (panel) { - NSString *message = @(msg_text); - [panel updateStatus: message]; + NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; + NSString *msgShort = msg_text_short ? @(msg_text_short) : nil; + [panel updateStatusLong: msgLong statusShort: msgShort]; } } @@ -79,7 +80,7 @@ void notification_handler(void* context_object, RimeSessionId session_id, const char* schema_name = strchr(message_value, '/'); if (schema_name) { ++schema_name; - show_status_message(schema_name, message_type); + show_status_message(schema_name, schema_name, message_type); } return; } @@ -87,9 +88,12 @@ void notification_handler(void* context_object, RimeSessionId session_id, if (!strcmp(message_type, "option")) { Bool state = message_value[0] != '!'; const char* option_name = message_value + !state; - const char *state_label = rime_get_api()->get_state_label(session_id, option_name, state); - if (state_label) { - show_status_message(state_label, message_type); + struct rime_string_slice_t state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); + struct rime_string_slice_t state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); + + if (state_label_long.str || state_label_short.str) { + const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; + show_status_message(state_label_long.str, short_message, message_type); } } } @@ -136,7 +140,7 @@ - (void)shutdownRime { -(void)loadSettings { _config = [[SquirrelConfig alloc] init]; - if (!_config.openBaseConfig) { + if (![_config openBaseConfig]) { return; } @@ -181,13 +185,13 @@ -(BOOL)problematicLaunchDetected options:NSDataReadingUncached error:nil]; if (archive) { - NSDate* previousLaunch = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; + NSDate* previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate* now = [NSDate date]; - NSData* record = [NSKeyedArchiver archivedDataWithRootObject:now]; + NSData* record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; [record writeToFile:logfile atomically:NO]; return detected; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index b91a384d8..c4bd50145 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -9,7 +9,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; @property(nonatomic, copy) NSString *colorSpace; @property(nonatomic, readonly) NSString *schemaId; -@property (NS_NONATOMIC_IOSONLY, readonly) BOOL openBaseConfig; +- (BOOL)openBaseConfig; - (BOOL)openWithSchemaId:(NSString *)schemaId baseConfig:(SquirrelConfig *)config; - (void)close; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 698f350e7..d816f1b40 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -155,7 +155,7 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { #pragma mark - Private methods - (id)cachedValueOfClass:(Class)aClass forKey:(NSString *)key { - id value = _cache[key]; + id value = [_cache objectForKey:key]; if (value && [value isKindOfClass:aClass]) { return value; } diff --git a/SquirrelInputController.m b/SquirrelInputController.m index a89d1c60c..cfcb3b134 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -4,7 +4,6 @@ #import "SquirrelConfig.h" #import "SquirrelPanel.h" #import "macos_keycode.h" -#import "utf8.h" #import #import @@ -90,6 +89,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } int release_mask = 0; NSUInteger changes = _lastModifier ^ modifiers; + _lastModifier = modifiers; if (changes & OSX_CAPITAL_MASK) { if (!keyCodeAvailable) { rime_keycode = XK_Caps_Lock; @@ -138,7 +138,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; - _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -168,12 +167,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender [self rimeUpdate]; } } break; -// case NSEventTypeLeftMouseDown: { -// if (_preeditString.length) { -// [_currentClient makeFirstResponder:nil]; -// [self commitComposition:_currentClient]; -// } -// } break; default: break; } @@ -352,7 +345,7 @@ -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { - if ([_preeditString isEqualToString:@" "]) { + if ([_preeditString isEqualToString:@"\u3000"]) { const char* raw_input = rime_get_api()->get_input(_session); if (raw_input) [self commitString:@(raw_input)]; @@ -459,6 +452,8 @@ -(void)showPanelWithPreedit:(NSString*)preedit comments:(NSArray*)comments labels:(NSArray*)labels highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; @@ -472,7 +467,9 @@ -(void)showPanelWithPreedit:(NSString*)preedit candidates:candidates comments:comments labels:labels - highlighted:index]; + highlighted:index + pageNum:pageNum + lastPage:lastPage]; } @end // SquirrelController @@ -529,6 +526,13 @@ -(void)rimeConsumeCommittedText } } +NSString *substr(const char *str, int length) { + char substring[length+1]; + strncpy(substring, str, length); + substring[length] = '\0'; + return [NSString stringWithCString:substring encoding:NSUTF8StringEncoding]; +} + -(void)rimeUpdate { //NSLog(@"rimeUpdate"); @@ -544,7 +548,8 @@ -(void)rimeUpdate _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || rime_get_api()->get_option(_session, "inline"); - _inlineCandidate = NSApp.squirrelAppDelegate.panel.inlineCandidate; + _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && + !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); } @@ -557,9 +562,9 @@ -(void)rimeUpdate const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; - NSUInteger start = utf8len(preedit, ctx.composition.sel_start); - NSUInteger end = utf8len(preedit, ctx.composition.sel_end); - NSUInteger caretPos = utf8len(preedit, ctx.composition.cursor_pos); + NSUInteger start = substr(preedit, ctx.composition.sel_start).length; + NSUInteger end = substr(preedit, ctx.composition.sel_end).length; + NSUInteger caretPos = substr(preedit, ctx.composition.cursor_pos).length; NSRange selRange = NSMakeRange(start, end - start); if (_inlineCandidate) { const char *candidatePreview = ctx.commit_text_preview; @@ -620,7 +625,9 @@ -(void)rimeUpdate candidates:candidates comments:comments labels:labels - highlighted:ctx.menu.highlighted_candidate_index]; + highlighted:ctx.menu.highlighted_candidate_index + pageNum:ctx.menu.page_no + lastPage:ctx.menu.is_last_page]; rime_get_api()->free_context(&ctx); } else { [NSApp.squirrelAppDelegate.panel hide]; diff --git a/SquirrelPanel.h b/SquirrelPanel.h index f3b1b73f7..1b01aa9b2 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -22,11 +22,13 @@ candidates:(NSArray*)candidates comments:(NSArray*)comments labels:(NSArray*)labels - highlighted:(NSUInteger)index; + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage; -(void)hide; --(void)updateStatus:(NSString*)message; +-(void)updateStatusLong:(NSString*)messageLong statusShort:(NSString*)messageShort; -(void)loadConfig:(SquirrelConfig*)config forDarkMode:(BOOL)isDark; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 51506ba3a..265b813e6 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -86,12 +86,17 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSDictionary *commentHighlightedAttrs; @property(nonatomic, strong, readonly) NSDictionary *preeditAttrs; @property(nonatomic, strong, readonly) NSDictionary *preeditHighlightedAttrs; +@property(nonatomic, strong, readonly) NSDictionary *pagingAttrs; @property(nonatomic, strong, readonly) NSParagraphStyle *paragraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; +@property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; -@property(nonatomic, strong, readonly) NSString *prefixLabelFormat, *suffixLabelFormat; +@property(nonatomic, strong, readonly) NSString *prefixLabelFormat; +@property(nonatomic, strong, readonly) NSString *suffixLabelFormat; +@property(nonatomic, strong, readonly) NSString *statusMessageType; - (void)setCandidateFormat:(NSString *)candidateFormat; +- (void)setStatusMessageType:(NSString *)statusMessageType; - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedStripColor:(NSColor *)highlightedStripColor @@ -119,10 +124,12 @@ - (void) setAttrs:(NSMutableDictionary *)attrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs; +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle; + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle; @end @@ -154,6 +161,14 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } } +- (void)setStatusMessageType:(NSString *)type { + if ([type isEqualToString: @"long"] || [type isEqualToString: @"short"] || [type isEqualToString: @"mix"]) { + _statusMessageType = type; + } else { + _statusMessageType = @"mix"; + } +} + - (void)setBackgroundColor:(NSColor *)backgroundColor highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor @@ -172,7 +187,7 @@ - (void)setCornerRadius:(double)cornerRadius borderWidth:(double)borderWidth linespace:(double)linespace preeditLinespace:(double)preeditLinespace - alpha:(CGFloat)alpha + alpha:(double)alpha translucency:(BOOL)translucency linear:(BOOL)linear vertical:(BOOL)vertical @@ -199,7 +214,8 @@ - (void) setAttrs:(NSMutableDictionary *)attrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs { +preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs { _attrs = attrs; _labelAttrs = labelAttrs; _highlightedAttrs = highlightedAttrs; @@ -208,34 +224,37 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _commentHighlightedAttrs = commentHighlightedAttrs; _preeditAttrs = preeditAttrs; _preeditHighlightedAttrs = preeditHighlightedAttrs; + _pagingAttrs = pagingAttrs; } - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle { + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle { _paragraphStyle = paragraphStyle; _preeditParagraphStyle = preeditParagraphStyle; + _pagingParagraphStyle = pagingParagraphStyle; } @end @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextStorage *text; +@property(nonatomic, readonly) NSTextView *textView; @property(nonatomic, readonly) NSRange highlightedRange; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; +@property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; -@property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; -- (void)setText:(NSAttributedString *)text; -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange; +- (void) drawViewWith:(NSRange)highlightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -272,14 +291,16 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; } - // Use textStorage to store text and manage all text layout and draws + _textView = [[NSTextView alloc] initWithFrame:frameRect]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0.0; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - _text = [[NSTextStorage alloc] init]; - [_text addLayoutManager:layoutManager]; - layoutManager.backgroundLayoutEnabled = YES; + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + [_textView replaceTextContainer:textContainer]; + _textView.layoutManager.backgroundLayoutEnabled = YES; + _textView.layoutManager.usesFontLeading = NO; + _textView.layoutManager.typesetterBehavior = NSTypesetterBehavior_10_4; _defaultTheme = [[SquirrelTheme alloc] init]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { @@ -290,29 +311,27 @@ - (instancetype)initWithFrame:(NSRect)frameRect { // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_text.layoutManagers[0] boundingRectForGlyphRange:glyphRange inTextContainer:_text.layoutManagers[0].textContainers[0]]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; return rect; } -- (void)setText:(NSAttributedString *)text { - [_text setAttributedString:[text copy]]; -} - // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void)drawViewWith:(NSRange)hilightedRange - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange { - _highlightedRange = hilightedRange; +- (void) drawViewWith:(NSRange)highlightedRange + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange { + _highlightedRange = highlightedRange; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; + _pagingRange = pagingRange; self.needsDisplay = YES; } @@ -322,7 +341,7 @@ - (void)drawViewWith:(NSRange)hilightedRange return 1; } else if (number <= -2) { return -1; - }else { + } else { return number / 2; } } @@ -401,8 +420,8 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _text.layoutManagers[0]; - NSTextContainer *textContainer = layoutManager.textContainers[0]; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; @@ -419,18 +438,18 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe leadingRect->origin.x = rightEdge; leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; } - if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length > glyphRange.location+glyphRange.length) { + if (NSMaxRange(fullRangeInBoundingRect) > NSMaxRange(glyphRange)) { *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(glyphRange.location+glyphRange.length, fullRangeInBoundingRect.location+fullRangeInBoundingRect.length-glyphRange.location-glyphRange.length) - inTextContainer:textContainer]; + NSMakeRange(NSMaxRange(glyphRange), NSMaxRange(fullRangeInBoundingRect)-NSMaxRange(glyphRange)) + inTextContainer:textContainer]; if (!nearEmptyRect(*trailingRect)) { bodyRect->size.height -= trailingRect->size.height; } double leftEdge = NSMinX(*trailingRect); trailingRect->origin.x = bodyRect->origin.x; trailingRect->size.width = leftEdge - bodyRect->origin.x; - } else if (fullRangeInBoundingRect.location+fullRangeInBoundingRect.length == glyphRange.location+glyphRange.length) { - *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location+glyphRange.length-1 effectiveRange:NULL]; + } else if (NSMaxRange(fullRangeInBoundingRect) == NSMaxRange(glyphRange)) { + *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { *trailingRect = NSZeroRect; } else if (!nearEmptyRect(*trailingRect)) { @@ -438,14 +457,14 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } } NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; - lastLineRect.size.width = textContainer.containerSize.width - lastLineRect.origin.x; + lastLineRect.size.width = textContainer.size.width - lastLineRect.origin.x; NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; - NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; - while (lastLineRange.length>0 && (glyphProperty == NSGlyphPropertyElastic || glyphProperty == NSGlyphPropertyControlCharacter)) { + NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; + while (lastLineRange.length>0 && (glyphProperty & NSGlyphPropertyElastic && glyphProperty & NSGlyphPropertyControlCharacter)) { lastLineRange.length -= 1; - glyphProperty = [layoutManager propertyForGlyphAtIndex:lastLineRange.location+lastLineRange.length-1]; + glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; } - if (lastLineRange.location+lastLineRange.length == glyphRange.location+glyphRange.length) { + if (NSMaxRange(lastLineRange) == NSMaxRange(glyphRange)) { if (!nearEmptyRect(*trailingRect)) { *trailingRect = lastLineRect; } else { @@ -510,25 +529,6 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } -// Add gap between horizontal candidates -- (void)addGapBetweenHorizontalCandidates:(NSRect *)rect { - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; - rect->origin.x -= _seperatorWidth / 2; - } - } else if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth / 2; - } - } else { - if (!nearEmptyRect(*rect)) { - rect->size.width += _seperatorWidth; - rect->origin.x -= _seperatorWidth / 2; - } - } -} - // All draws happen here - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *backgroundPath; @@ -538,59 +538,67 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *highlightedPreeditPath; NSBezierPath *highlightedPreeditPath2; NSBezierPath *preeditPath; + NSBezierPath *pagingPath; + NSBezierPath *candidatePath; SquirrelTheme * theme = self.currentTheme; - NSRect textField = dirtyRect; - textField.origin.y += theme.edgeInset.height; - textField.origin.x += theme.edgeInset.width; - - // Draw preedit Rect + [NSBezierPath setDefaultLineWidth:0]; NSRect backgroundRect = dirtyRect; - + NSRect textField = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); + // Draw preedit Rect NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = textField.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - preeditRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, textField.origin.y - theme.edgeInset.height); + preeditRect.size.width = backgroundRect.size.width; + preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace; + preeditRect.origin = backgroundRect.origin; if (_highlightedRange.length == 0) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace / 2 - theme.hilitedCornerRadius / 2; + preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace; } if (theme.preeditBackgroundColor != nil) { preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); } } - // Draw highlighted Rect - if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { - NSRect innerBox = backgroundRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; - innerBox.origin.x += theme.edgeInset.width + 1; - if (_preeditRange.length == 0) { - innerBox.origin.y += theme.edgeInset.height + 1; - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.origin.y += preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 1; - innerBox.size.height -= theme.edgeInset.height + preeditRect.size.height + theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2 + 2; + // Draw paging Rect + NSRect pagingRect = NSZeroRect; + if (_pagingRange.length > 0) { + pagingRect = [self contentRectForRange:_pagingRange]; + pagingRect.size.width = backgroundRect.size.width; + pagingRect.size.height += theme.edgeInset.height; + pagingRect.origin.x = backgroundRect.origin.x; + pagingRect.origin.y += theme.edgeInset.height; + if (theme.preeditBackgroundColor != nil) { + pagingPath = drawSmoothLines(rectVertex(pagingRect), 0, 0); } - NSRect outerBox = backgroundRect; - outerBox.size.height -= theme.hilitedCornerRadius + preeditRect.size.height; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2 + preeditRect.size.height; + } + + // Draw candidate Rect + CGFloat halfLinespace = theme.linespace / 2; + NSRect candidateRect = backgroundRect; + candidateRect.size.height -= preeditRect.size.height + pagingRect.size.height; + candidateRect.origin.y += preeditRect.size.height; + if (_preeditRange.length == 0) { + candidateRect.size.height -= theme.edgeInset.height/2; + candidateRect.origin.y += theme.edgeInset.height/2; + } + + NSRect outerBox = NSInsetRect(candidateRect, theme.edgeInset.width/2, 0); + NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2+1, halfLinespace+1); + + if (theme.preeditBackgroundColor != NULL) { + candidatePath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + } - CGFloat halfLinespace = theme.linespace / 2; - if (theme.linear){ + // Drawhighlighted Rect + if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { + if (theme.linear) { NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - [self addGapBetweenHorizontalCandidates:&leadingRect]; - [self addGapBetweenHorizontalCandidates:&bodyRect]; - [self addGapBetweenHorizontalCandidates:&trailingRect]; - NSMutableArray *highlightedPoints; NSMutableArray *highlightedPoints2; // Handles the special case where containing boxes are separated @@ -601,12 +609,12 @@ - (void)drawRect:(NSRect)dirtyRect { highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); - xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); - innerBox.size.height -= halfLinespace; +// xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); +// xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); +// innerBox.size.height -= halfLinespace; // Expand the boxes to reach proper border - expand(highlightedPoints, innerBox, outerBox); - expand(highlightedPoints2, innerBox, outerBox); +// expand(highlightedPoints, innerBox, outerBox); +// expand(highlightedPoints2, innerBox, outerBox); highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPoints2.count > 0) { highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); @@ -614,21 +622,21 @@ - (void)drawRect:(NSRect)dirtyRect { } else { NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; highlightedRect.size.width = textField.size.width; - highlightedRect.size.height += theme.linespace; - highlightedRect.origin = NSMakePoint(textField.origin.x - theme.edgeInset.width, - highlightedRect.origin.y + theme.edgeInset.height - halfLinespace); - if (_highlightedRange.location+_highlightedRange.length == _text.length) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - } - if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - if (_preeditRange.length == 0) { - highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - } else { - highlightedRect.size.height += theme.hilitedCornerRadius / 2; - highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - } - } +// highlightedRect.size.height += halfLinespace; + highlightedRect.origin.x = textField.origin.x; + highlightedRect.origin.y += theme.edgeInset.height; + // if (_highlightedRange.location+_highlightedRange.length == _text.length) { + // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + // } + // if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { + // if (_preeditRange.length == 0) { + // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; + // highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; + // } else { + // highlightedRect.size.height += theme.hilitedCornerRadius / 2; + // highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; + // } + // } NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; expand(highlightedPoints, innerBox, outerBox); highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); @@ -642,20 +650,16 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + NSRect outerBox = preeditRect; + outerBox.size.width -= theme.edgeInset.width; + outerBox.size.height -= theme.edgeInset.height/2 + theme.hilitedCornerRadius/2; + outerBox.origin.x += theme.edgeInset.width/2; + outerBox.origin.y += theme.edgeInset.height/2; NSRect innerBox = preeditRect; - innerBox.size.width -= (theme.edgeInset.width + 1) * 2; + innerBox.size.width -= theme.edgeInset.width * 2 + 2; + innerBox.size.height -= theme.edgeInset.height * 2 + theme.preeditLinespace; innerBox.origin.x += theme.edgeInset.width + 1; innerBox.origin.y += theme.edgeInset.height + 1; - if (_highlightedRange.length == 0) { - innerBox.size.height -= (theme.edgeInset.height + 1) * 2; - } else { - innerBox.size.height -= theme.edgeInset.height + theme.preeditLinespace + theme.hilitedCornerRadius / 2 + 2; - } - NSRect outerBox = preeditRect; - outerBox.size.height -= theme.hilitedCornerRadius; - outerBox.size.width -= theme.hilitedCornerRadius; - outerBox.origin.x += theme.hilitedCornerRadius / 2; - outerBox.origin.y += theme.hilitedCornerRadius / 2; NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; @@ -675,16 +679,17 @@ - (void)drawRect:(NSRect)dirtyRect { } } - [NSBezierPath setDefaultLineWidth:0]; - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), theme.cornerRadius*0.3, theme.cornerRadius*1.4); + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); _shape.path = backgroundPath.quartzPath; - // Nothing should extend beyond backgroundPath - borderPath = [backgroundPath copy]; - [borderPath addClip]; + + NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); + borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); borderPath.lineWidth = theme.borderWidth; + // Nothing should extend beyond backgroundPath + [backgroundPath addClip]; -// This block of code enables independent transparencies in highlighted colour and background colour. -// Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. + // This block of code enables independent transparencies in highlighted colour and background colour. + // Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. #if 0 // Calculate intersections. if (![highlightedPath isEmpty]) { @@ -716,11 +721,19 @@ - (void)drawRect:(NSRect)dirtyRect { [preeditPath setWindingRule:NSEvenOddWindingRule]; #endif - [theme.backgroundColor setFill]; - [backgroundPath fill]; - if (theme.preeditBackgroundColor && ![preeditPath isEmpty]) { + if (theme.preeditBackgroundColor && + (_preeditRange.length > 0 || _highlightedRange.length > 0)) { [theme.preeditBackgroundColor setFill]; - [preeditPath fill]; + [backgroundPath fill]; + if (_highlightedRange.length > 0){ + [[NSColor clearColor] setFill]; + [candidatePath fill]; + [theme.backgroundColor setFill]; + [candidatePath fill]; + } + } else { + [theme.backgroundColor setFill]; + [backgroundPath fill]; } if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { [theme.highlightedStripColor setFill]; @@ -736,13 +749,14 @@ - (void)drawRect:(NSRect)dirtyRect { [highlightedPreeditPath2 fill]; } } - if (theme.borderColor && (theme.borderWidth > 0)) { [theme.borderColor setStroke]; [borderPath stroke]; } - NSRange glyphRange = [_text.layoutManagers[0] glyphRangeForTextContainer:_text.layoutManagers[0].textContainers[0]]; - [_text.layoutManagers[0] drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; + [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + } @end @@ -754,6 +768,7 @@ @implementation SquirrelPanel { NSRange _preeditRange; NSRect _screenRect; CGFloat _maxHeight; + CGFloat _maxTextWidth; NSString *_statusMessage; NSTimer *_statusTimer; @@ -775,55 +790,18 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -CGFloat minimumHeight(NSDictionary *attribute) { - const NSAttributedString *spaceChar = [[NSAttributedString alloc] initWithString:@" " attributes:attribute]; - const CGFloat minimumHeight = [spaceChar boundingRectWithSize:NSZeroSize options:0].size.height; - return minimumHeight; -} - -// Use this method to convert charcters to upright position -// Based on the width of the chacter, relative font size matters -void convertToVerticalGlyph(NSMutableAttributedString *originalText, NSRange stringRange) { - NSDictionary *attribute = [originalText attributesAtIndex:stringRange.location effectiveRange:NULL]; - double baseOffset = [attribute[NSBaselineOffsetAttributeName] doubleValue]; - // Use the width of the character to determin if they should be upright in vertical writing mode. - // Adjust font base line for better alignment. - const NSAttributedString *cjkChar = [[NSAttributedString alloc] initWithString:@"字" attributes:attribute]; - const NSRect cjkRect = [cjkChar boundingRectWithSize:NSZeroSize options:0]; - const NSAttributedString *hangulChar = [[NSAttributedString alloc] initWithString:@"글" attributes:attribute]; - const NSSize hangulSize = [hangulChar boundingRectWithSize:NSZeroSize options:0].size; - stringRange = [originalText.string rangeOfComposedCharacterSequencesForRange:stringRange]; - NSUInteger i = stringRange.location; - while (i < stringRange.location+stringRange.length) { - NSRange range = [originalText.string rangeOfComposedCharacterSequenceAtIndex:i]; - i = range.location + range.length; - NSRect charRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - // Also adjust the baseline so upright and lying charcters are properly aligned - if ((charRect.size.width >= cjkRect.size.width) || (charRect.size.width >= hangulSize.width)) { - [originalText addAttribute:NSVerticalGlyphFormAttributeName value:@(1) range:range]; - NSRect uprightCharRect = [[originalText attributedSubstringFromRange:range] boundingRectWithSize:NSZeroSize options:0]; - CGFloat widthDiff = charRect.size.width-cjkChar.size.width; - CGFloat offset = (cjkRect.size.height - uprightCharRect.size.height)/2 + (cjkRect.origin.y-uprightCharRect.origin.y) - (widthDiff>0 ? widthDiff/3 : widthDiff/2) +baseOffset; - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:range]; - } else { - [originalText addAttribute:NSBaselineOffsetAttributeName value:@(baseOffset) range:range]; - } - } -} - void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); long i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; - NSNumber *baselineOffset = @(charFont.pointSize * -0.1); if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { - NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; + NSFontWeight fontWeight = [[charFont.fontDescriptor.fontAttributes objectForKey:NSFontWeightTrait] doubleValue]; + NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize weight:NSFontWeightThin]; [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; - [text addAttribute:NSBaselineOffsetAttributeName value:baselineOffset range:currentFontRange]; } - i = currentFontRange.location + currentFontRange.length; + i = NSMaxRange(currentFontRange); } } @@ -847,32 +825,40 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { attrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *highlightedAttrs = [[NSMutableDictionary alloc] init]; - highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedControlTextColor]; + highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedMenuItemTextColor]; highlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; - labelAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize - weight:NSFontWeightRegular]; + labelAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; + labelAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; + NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; - labelHighlightedAttrs[NSFontAttributeName] = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize - weight:NSFontWeightRegular]; + labelHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + labelHighlightedAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; commentAttrs[NSForegroundColorAttributeName] = secondaryTextColor; commentAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; - NSMutableDictionary *commentHighlightedAttrs = [commentAttrs mutableCopy]; + NSMutableDictionary *commentHighlightedAttrs = [[NSMutableDictionary alloc] init]; + commentHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + commentHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; - preeditAttrs[NSForegroundColorAttributeName] = secondaryTextColor; + preeditAttrs[NSForegroundColorAttributeName] = [NSColor textColor]; preeditAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; NSMutableDictionary *preeditHighlightedAttrs = [[NSMutableDictionary alloc] init]; - preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; + preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedTextColor]; preeditHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; + pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; + pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize/1.5]; + NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; + NSParagraphStyle *pagingParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs labelAttrs:labelAttrs @@ -881,9 +867,11 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs]; + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs]; [theme setParagraphStyle:paragraphStyle - preeditParagraphStyle:preeditParagraphStyle]; + preeditParagraphStyle:preeditParagraphStyle + pagingParagraphStyle:pagingParagraphStyle]; } - (instancetype)init { @@ -950,68 +938,76 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = _view.text.size.width; + CGFloat textWidth = _view.textView.textStorage.size.width; NSFont *currentFont = theme.attrs[NSFontAttributeName]; CGFloat fontScale = currentFont.pointSize / 12; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); - CGFloat maxTextWidth = theme.vertical + _maxTextWidth = theme.vertical ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - if (textWidth > maxTextWidth) { - textWidth = maxTextWidth; + if (textWidth > _maxTextWidth) { + textWidth = _maxTextWidth; } - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(textWidth, 0); + _view.textView.textContainer.containerSize = NSMakeSize(textWidth, 0); NSRect windowRect; // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; - if ((theme.vertical && NSMidY(_position) / NSHeight(_screenRect) < 0.5) || + if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.text.layoutManagers[0].textContainers[0].containerSize = NSMakeSize(_maxHeight, 0); + _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, 0); } } + // the sweep direction of the client app changes the behavior of adjusting squirrel panel position + bool sweepVertical = NSWidth(_position) > NSHeight(_position); if (theme.vertical) { windowRect.size = NSMakeSize(contentRect.size.height + theme.edgeInset.height * 2, contentRect.size.width + theme.edgeInset.width * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMidY(_position) / NSHeight(_screenRect) >= 0.5) { - windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); + if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { + windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : kOffsetHeight) - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + kOffsetHeight; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight) + theme.edgeInset.width; } // Make the first candidate fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - windowRect.size.width - kOffsetHeight; - if (_preeditRange.length > 0) { + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); + if (!sweepVertical && _preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_preeditRange].size; - windowRect.origin.x += preeditSize.height + theme.edgeInset.width; + windowRect.origin.x += preeditSize.height + theme.edgeInset.width / 2; } } else { windowRect.size = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, contentRect.size.height + theme.edgeInset.height * 2); - windowRect.origin = NSMakePoint(NSMinX(_position), - NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + if (sweepVertical) { + // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa + if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + } else { + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width; + } + windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); + } else { + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width / 2, + NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + } } - if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { // can only happen with horizontal - windowRect.origin.x = NSMinX(_position) - NSWidth(windowRect) - kOffsetHeight; + if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { + windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); } - if (NSMinX(windowRect) < NSMinX(_screenRect)) { // can only happen with vertical - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + if (NSMinX(windowRect) < NSMinX(_screenRect)) { + windowRect.origin.x = sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(_screenRect); } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - if (theme.vertical) { // failed attempt: NSMaxY(_position) + kOffsetHeight; - windowRect.origin.y = NSMinY(_screenRect); - } else { - windowRect.origin.y = NSMaxY(_position) + kOffsetHeight; - } + windowRect.origin.y = (sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position) + kOffsetHeight); } - if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { // failed attempt: NSMaxY(_position) + kOffsetHeight; - windowRect.origin.y = NSMaxY(_screenRect) - NSHeight(windowRect); + if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { + windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); } [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! @@ -1055,7 +1051,9 @@ - (void)showPreedit:(NSString *)preedit candidates:(NSArray *)candidates comments:(NSArray *)comments labels:(NSArray *)labels - highlighted:(NSUInteger)index { + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { NSUInteger numCandidates = candidates.count; if (numCandidates || (preedit && preedit.length)) { _statusMessage = nil; @@ -1081,197 +1079,206 @@ - (void)showPreedit:(NSString *)preedit NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); // preedit if (preedit) { - NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; + NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { - NSUInteger highlightedPreeditStart = line.length; - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping - attributes:theme.preeditHighlightedAttrs]]; - highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, line.length - highlightedPreeditStart); - } - if (selRange.location + selRange.length < preedit.length) { - [line - appendAttributedString: - [[NSAttributedString alloc] - initWithString:[preedit substringFromIndex:selRange.location + - selRange.length].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + NSUInteger highlightedPreeditStart = preeditLine.length; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping + attributes:theme.preeditHighlightedAttrs]]; + highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); } - [text appendAttributedString:line]; - - NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; - if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, line.length)); - paragraphStylePreedit.minimumLineHeight = minimumHeight(theme.preeditAttrs); + if (NSMaxRange(selRange) < preedit.length) { + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringFromIndex:NSMaxRange(selRange)].precomposedStringWithCanonicalMapping + attributes:theme.preeditAttrs]]; } - [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit - range:NSMakeRange(0, text.length)]; + fixDefaultFont(preeditLine); + NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; + [preeditLine addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePreedit + range:NSMakeRange(0, preeditLine.length)]; + + [text appendAttributedString:preeditLine]; _preeditRange = NSMakeRange(0, text.length); if (numCandidates) { - [text appendAttributedString:[[NSAttributedString alloc] - initWithString:@"\n" - attributes:theme.preeditAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" + attributes:theme.preeditAttrs]]; } candidateStartPos = text.length; } - NSRange highlightedRange = NSMakeRange(NSNotFound, 0); // candidates + CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + CGFloat candidateTextWidth = 0 - separatorWidth; + NSRange highlightedRange = NSMakeRange(NSNotFound, 0); NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; - NSDictionary *labelAttrs = - (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; - NSDictionary *commentAttrs = - (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; + NSDictionary *labelAttrs = (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; + NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { - NSString *labelString; + NSString *prefixLabelString; if (labels.count > 1 && i < labels.count) { NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - labelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... char labelCharacter = [labels[0] characterAtIndex:i]; - labelString = [NSString stringWithFormat:theme.prefixLabelFormat, labelCharacter]; + prefixLabelString = [[NSString stringWithFormat:theme.prefixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } else { // default: 1. 2. 3... NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; + prefixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:labelString - attributes:labelAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString + attributes:labelAttrs]]; // get the label size for indent - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(0, line.length)); - } if (!theme.linear) { - labelWidth = [line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin].size.width; + labelWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); } } NSUInteger candidateStart = line.length; NSString *candidate = candidates[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:candidate.precomposedStringWithCanonicalMapping + attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(candidateStart, line.length-candidateStart)); + + if (i < comments.count && [comments[i] length] != 0) { + NSUInteger commentStart = line.length; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " + attributes:commentAttrs]]; + NSString *comment = comments[i]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCanonicalMapping + attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { - NSString *labelString; + NSString *suffixLabelString; if (labels.count > 1 && i < labels.count) { NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - labelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... char labelCharacter = [labels[0] characterAtIndex:i]; - labelString = [NSString stringWithFormat:theme.suffixLabelFormat, labelCharacter]; + suffixLabelString = [[NSString stringWithFormat:theme.suffixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } else { // default: 1. 2. 3... NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - labelString = [NSString stringWithFormat:labelFormat, (i+1) % 10]; + suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } NSUInteger suffixLabelStart = line.length; - [line appendAttributedString: - [[NSAttributedString alloc] - initWithString:labelString - attributes:labelAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(suffixLabelStart, line.length-suffixLabelStart)); - } - } - - if (i < comments.count && [comments[i] length] != 0) { - NSUInteger commentStart = line.length; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; - NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] - initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]]; - if (theme.vertical) { - convertToVerticalGlyph(line, NSMakeRange(commentStart, line.length-commentStart)); + [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString + attributes:attrs]]; + } + + fixDefaultFont(line); + // determine if the line is too wide and line break is needed, based on screen size. + NSString *separtatorString = @"\u2029"; + if (theme.linear) { + CGFloat lineWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + candidateTextWidth += separatorWidth + lineWidth; + if (candidateTextWidth > _maxTextWidth) { + separtatorString = @"\u2028"; + candidateTextWidth = lineWidth; + } else { + separtatorString = @" "; } } + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:attrs]; - NSAttributedString *separator = [[NSMutableAttributedString alloc] - initWithString:(theme.linear ? @" " : @"\n") - attributes:attrs]; - _view.seperatorWidth = [separator boundingRectWithSize:NSZeroSize options:0].size.width; - - NSMutableParagraphStyle *paragraphStyleCandidate; - if (i == 0) { - NSMutableParagraphStyle *firstParagraphStyle = [theme.paragraphStyle mutableCopy]; - firstParagraphStyle.paragraphSpacingBefore = theme.preeditLinespace / 2 + theme.hilitedCornerRadius / 2; - paragraphStyleCandidate = firstParagraphStyle; - } else { - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + if (i > 0) { [text appendAttributedString:separator]; } if (!theme.linear) { -// paragraphStyleCandidate.lineSpacing = theme.linespace; paragraphStyleCandidate.headIndent = labelWidth; } - if (theme.vertical) { - paragraphStyleCandidate.minimumLineHeight = minimumHeight(attrs); - } - // Use left-to-right marks to declare the default writing direction and - // prevent strong right-to-left characters from setting the wirint direction - paragraphStyleCandidate.baseWritingDirection = NSWritingDirectionLeftToRight; - - paragraphStyleCandidate.headIndent = labelWidth; - [line addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, line.length)]; - + [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; if (i == index) { highlightedRange = NSMakeRange(text.length, line.length); } [text appendAttributedString:line]; } - // Fix font rendering - fixDefaultFont(text); + // paging indication + NSRange pagingRange = NSMakeRange(NSNotFound, 0); + + if (numCandidates) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; + + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" + attributes:theme.pagingAttrs]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(pageNum ? @"◀" : @"◁") + attributes:theme.pagingAttrs]]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] + attributes:theme.pagingAttrs]]; + + [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(lastPage ? @"▷" : @"▶") + attributes:theme.pagingAttrs]]; + + fixDefaultFont(paging); + NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; + [paging addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePaging + range:NSMakeRange(0, paging.length)]; + + pagingRange = NSMakeRange(text.length, paging.length); + [text appendAttributedString:paging]; + } + + [_view.textView.textStorage setAttributedString:text]; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; // text done! - [_view setText:text]; - [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange]; + [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange]; + [self show]; } -- (void)updateStatus:(NSString *)message { - _statusMessage = message; +- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { + SquirrelTheme *theme = _view.currentTheme; + if ([theme.statusMessageType isEqualToString:@"mix"]) { + if (messageShort) { + _statusMessage = messageShort; + } else { + _statusMessage = messageLong; + } + } else if ([theme.statusMessageType isEqualToString:@"long"]) { + _statusMessage = messageLong; + } else if ([theme.statusMessageType isEqualToString:@"short"]) { + if (messageShort) { + _statusMessage = messageShort; + } else if (messageLong) { + _statusMessage = [messageLong substringWithRange:[messageLong rangeOfComposedCharacterSequenceAtIndex:0]]; + } + } } - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]; - if (theme.vertical) { - convertToVerticalGlyph(text, NSMakeRange(0, text.length)); - } - [_view setText:text]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; + fixDefaultFont(text); + + [_view.textView.textStorage setAttributedString:text]; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange]; + [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange]; [self show]; if (_statusTimer) { @@ -1370,21 +1377,22 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; + NSString *statusMessageType = [config getString:@"style/status_message_type"]; NSString *candidateFormat = [config getString:@"style/candidate_format"]; NSString *fontName = [config getString:@"style/font_face"]; - NSInteger fontSize = [config getDouble:@"style/font_point"]; + CGFloat fontSize = [config getDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSInteger labelFontSize = [config getDouble:@"style/label_font_point"]; + CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSInteger commentFontSize = [config getDouble:@"style/comment_font_point"]; + CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; - CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; - CGFloat spacing = [config getDouble:@"style/spacing"]; + CGFloat lineSpacing = fmax([config getDouble:@"style/line_spacing"], hilitedCornerRadius); + CGFloat spacing = fmax([config getDouble:@"style/spacing"], hilitedCornerRadius); CGFloat baseOffset = [config getDouble:@"style/base_offset"]; NSColor *backgroundColor; @@ -1482,7 +1490,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *fontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; if (fontSizeOverridden) { - fontSize = fontSizeOverridden.integerValue; + fontSize = fontSizeOverridden.doubleValue; } NSString *labelFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; @@ -1492,7 +1500,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *labelFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; if (labelFontSizeOverridden) { - labelFontSize = labelFontSizeOverridden.integerValue; + labelFontSize = labelFontSizeOverridden.doubleValue; } NSString *commentFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; @@ -1502,7 +1510,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSNumber *commentFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; if (commentFontSizeOverridden) { - commentFontSize = commentFontSizeOverridden.integerValue; + commentFontSize = commentFontSizeOverridden.doubleValue; } NSColor *candidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_color"]]; @@ -1585,22 +1593,24 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } NSFontDescriptor *labelFontDescriptor = nil; NSFont *labelFont = nil; + NSMutableDictionary *labelFontAttr; + labelFontAttr[NSFontFixedAdvanceAttribute] = @(labelFontSize); + labelFontAttr[NSFontSizeAttribute] = @(labelFontSize); if (labelFontName != nil) { labelFontDescriptor = getFontDescriptor(labelFontName); if (labelFontDescriptor == nil) { - labelFontDescriptor = fontDescriptor; + labelFontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr]; } if (labelFontDescriptor != nil) { labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; } } if (labelFont == nil) { -// if (fontDescriptor != nil) { -// labelFont = [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize]; -// } else { -// labelFont = [NSFont fontWithName:font.fontName size:labelFontSize]; -// } - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + if (fontDescriptor != nil) { + labelFont = [NSFont fontWithDescriptor:[fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr] size:labelFontSize]; + } else { + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + } } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1620,24 +1630,27 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; } } + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize/1.5]; - CGFloat lineHeight = fmax(fmax(fontSize, labelFontSize), commentFontSize) * 1.15; + CGFloat lineHeight = font.ascender - font.descender; + NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + paragraphStyle.alignment = NSTextAlignmentJustified; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2; + paragraphStyle.lineSpacing = lineSpacing/2; - NSMutableParagraphStyle *paragraphStyle = - [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing; - paragraphStyle.maximumLineHeight = lineHeight + lineSpacing; -// paragraphStyle.paragraphSpacing = lineSpacing / 2; -// paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; - paragraphStyle.lineSpacing = lineSpacing; + NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + preeditParagraphStyle.alignment = NSTextAlignmentLeft; + preeditParagraphStyle.minimumLineHeight = lineHeight; + preeditParagraphStyle.paragraphSpacing = spacing; - NSMutableParagraphStyle *preeditParagraphStyle = - [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - preeditParagraphStyle.minimumLineHeight = fontSize * 1.15 + spacing; - preeditParagraphStyle.maximumLineHeight = fontSize * 1.15 + spacing; -// preeditParagraphStyle.paragraphSpacing = spacing + hilitedCornerRadius - lineSpacing; - preeditParagraphStyle.lineSpacing = spacing; - preeditParagraphStyle.paragraphSpacing = hilitedCornerRadius; + NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + pagingParagraphStyle.alignment = NSTextAlignmentRight; + + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left + // characters from setting the writing direction in case the label are direction-less symbols + paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -1647,6 +1660,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSMutableDictionary *commentHighlightedAttrs = [theme.commentHighlightedAttrs mutableCopy]; NSMutableDictionary *preeditAttrs = [theme.preeditAttrs mutableCopy]; NSMutableDictionary *preeditHighlightedAttrs = [theme.preeditHighlightedAttrs mutableCopy]; + NSMutableDictionary *pagingAttrs = [theme.pagingAttrs mutableCopy]; attrs[NSFontAttributeName] = font; highlightedAttrs[NSFontAttributeName] = font; @@ -1656,6 +1670,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSFontAttributeName] = commentFont; preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; + pagingAttrs[NSFontAttributeName] = pagingFont; attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); @@ -1664,21 +1679,26 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSSuperscriptAttributeName] = @(-1); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; - backgroundColor = backgroundColor ? backgroundColor : [NSColor windowBackgroundColor]; + backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; + borderColor = borderColor ? borderColor : [NSColor gridColor]; + preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : [NSColor windowBackgroundColor]; candidateTextColor = candidateTextColor ? candidateTextColor : [NSColor controlTextColor]; + highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedMenuItemTextColor]; + highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : [NSColor selectedContentBackgroundColor]; candidateLabelColor = candidateLabelColor ? candidateLabelColor : - isNative ? secondaryTextColor : blendColors(candidateTextColor, backgroundColor); - highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedControlTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : [NSColor selectedTextBackgroundColor]; + isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : - isNative ? secondaryTextColor : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); + isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); commentTextColor = commentTextColor ? commentTextColor : secondaryTextColor; - highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : commentTextColor; - textColor = textColor ? textColor : secondaryTextColor; - highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor controlTextColor]; + highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : [NSColor alternateSelectedControlTextColor]; + textColor = textColor ? textColor : [NSColor textColor]; + highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor selectedTextColor]; + highlightedBackColor = highlightedBackColor ? highlightedBackColor : [NSColor selectedTextBackgroundColor]; attrs[NSForegroundColorAttributeName] = candidateTextColor; labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; @@ -1688,7 +1708,20 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; preeditAttrs[NSForegroundColorAttributeName] = textColor; preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; - + pagingAttrs[NSForegroundColorAttributeName] = candidateLabelColor; + + attrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + labelAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + highlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + preeditAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + + [theme setStatusMessageType:statusMessageType]; + [theme setAttrs:attrs labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs @@ -1696,10 +1729,12 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs]; + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs]; [theme setParagraphStyle:paragraphStyle - preeditParagraphStyle:preeditParagraphStyle]; + preeditParagraphStyle:preeditParagraphStyle + pagingParagraphStyle:pagingParagraphStyle]; [theme setBackgroundColor:backgroundColor highlightedStripColor:highlightedCandidateBackColor @@ -1707,17 +1742,14 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - NSSize edgeInset; - if (vertical) { - edgeInset = NSMakeSize(MAX(borderHeight, cornerRadius), MAX(borderWidth, cornerRadius)); - } else { - edgeInset = NSMakeSize(MAX(borderWidth, cornerRadius), MAX(borderHeight, cornerRadius)); - } + borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)); + borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)); + NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); [theme setCornerRadius:cornerRadius hilitedCornerRadius:hilitedCornerRadius edgeInset:edgeInset - borderWidth:MIN(borderHeight, borderWidth) + borderWidth:MAX(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) @@ -1725,9 +1757,10 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo linear:linear vertical:vertical inlinePreedit:inlinePreedit - inlineCandidate:inlineCandidate]; + inlineCandidate:inlineCandidate]; theme.native = isNative; theme.candidateFormat = (candidateFormat ? candidateFormat : kDefaultCandidateFormat); } + @end From d3672077aef3d731d8b4b37355453d790e0fbda7 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 21 Apr 2023 22:57:12 +0200 Subject: [PATCH 048/164] Sparkle --- Sparkle | 1 + 1 file changed, 1 insertion(+) create mode 160000 Sparkle diff --git a/Sparkle b/Sparkle new file mode 160000 index 000000000..8fb9c83ad --- /dev/null +++ b/Sparkle @@ -0,0 +1 @@ +Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 From ee84b24637a3ba6a18434352aa68285e76c28679 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 21 Apr 2023 23:21:28 +0200 Subject: [PATCH 049/164] paging optional --- SquirrelPanel.m | 17 ++++++++++++++++- data/squirrel.yaml | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 265b813e6..680ebde1c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -73,6 +73,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; +@property(nonatomic, readonly) BOOL showPaging; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -112,6 +113,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(BOOL)translucency + showPaging:(BOOL)showPaging linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -189,6 +191,7 @@ - (void)setCornerRadius:(double)cornerRadius preeditLinespace:(double)preeditLinespace alpha:(double)alpha translucency:(BOOL)translucency + showPaging:(BOOL)showPaging linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -200,6 +203,7 @@ - (void)setCornerRadius:(double)cornerRadius _linespace = linespace; _alpha = alpha; _translucency = translucency; + _showPaging = showPaging; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -790,6 +794,10 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } +- (BOOL)showPaging { + return _view.currentTheme.showPaging; +} + void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -1216,7 +1224,7 @@ - (void)showPreedit:(NSString *)preedit // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); - if (numCandidates) { + if (numCandidates && theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" @@ -1377,6 +1385,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; + BOOL showPaging = [config getBool:@"style/show_paging"]; NSString *statusMessageType = [config getString:@"style/status_message_type"]; NSString *candidateFormat = [config getString:@"style/candidate_format"]; @@ -1476,6 +1485,11 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } + NSNumber *showPagingOverridden = + [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]]; + if (showPagingOverridden) { + showPaging = showPagingOverridden.boolValue; + } NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { @@ -1754,6 +1768,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency + showPaging:showPaging linear:linear vertical:vertical inlinePreedit:inlinePreedit diff --git a/data/squirrel.yaml b/data/squirrel.yaml index a9cce2041..0e899c8e3 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -28,6 +28,7 @@ style: #candidate_list_layout: stacked # stacked | linear text_orientation: horizontal # horizontal | vertical inline_preedit: true + show_paging : true corner_radius: 10 hilited_corner_radius: 0 From 176f5ce1a5474c1607d646f08d057d0ea1432b80 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 22 Apr 2023 16:12:16 +0200 Subject: [PATCH 050/164] selection by clicking --- SquirrelInputController.h | 2 +- SquirrelInputController.m | 20 ++- SquirrelPanel.h | 11 +- SquirrelPanel.m | 335 ++++++++++++++++++++++++++++++-------- 4 files changed, 295 insertions(+), 73 deletions(-) diff --git a/SquirrelInputController.h b/SquirrelInputController.h index a857f893b..eee4a33d7 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -2,5 +2,5 @@ #import @interface SquirrelInputController : IMKInputController - +- (BOOL)selectCandidate:(NSInteger)index; @end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index cfcb3b134..eaaf10275 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -230,6 +230,21 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers return handled; } +- (BOOL)selectCandidate:(NSInteger)index { + BOOL handled = NO; + if (index == NSPageUpFunctionKey) { + handled = rime_get_api()->process_key(_session, XK_Page_Up, 0); + } else if (index == NSPageDownFunctionKey) { + handled = rime_get_api()->process_key(_session, XK_Page_Down, 0); + } else if (index >= 0 && index < 10) { + handled = rime_get_api()->select_candidate_on_current_page(_session, (int) index); + } + if (handled) { + [self rimeUpdate]; + } + return handled; +} + -(void)onChordTimer:(NSTimer *)timer { // chord release triggered by timer @@ -461,6 +476,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit [_currentClient attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; panel.position = inputPos; + panel.inputController = self; [panel showPreedit:preedit selRange:selRange caretPos:caretPos @@ -469,7 +485,9 @@ -(void)showPanelWithPreedit:(NSString*)preedit labels:labels highlighted:index pageNum:pageNum - lastPage:lastPage]; + lastPage:lastPage + buttonEffect:NSNotFound + update:YES]; } @end // SquirrelController diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 1b01aa9b2..ed63297b8 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -1,8 +1,8 @@ #import - +#import "SquirrelInputController.h" @class SquirrelConfig; -@interface SquirrelPanel : NSWindow +@interface SquirrelPanel : NSPanel // Linear candidate list, as opposed to stacked candidate list. @property(nonatomic, readonly) BOOL linear; @@ -12,10 +12,11 @@ @property(nonatomic, readonly) BOOL inlinePreedit; // Show first candidate inline @property(nonatomic, readonly) BOOL inlineCandidate; - // position of input caret on screen. @property(nonatomic, assign) NSRect position; +@property(nonatomic, assign) SquirrelInputController *inputController; + -(void)showPreedit:(NSString*)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos @@ -24,7 +25,9 @@ labels:(NSArray*)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage; + lastPage:(BOOL)lastPage + buttonEffect:(NSUInteger)buttonEffect + update:(BOOL)update; -(void)hide; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 307a23b58..d592274bc 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -244,21 +244,27 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView @property(nonatomic, readonly) NSTextView *textView; -@property(nonatomic, readonly) NSRange highlightedRange; +@property(nonatomic, readonly) NSArray *candidateRanges; +@property(nonatomic, readonly) NSInteger highlightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) NSMutableArray *candidatePathRefs; +@property(nonatomic, readonly) NSMutableArray *pagingRects; +@property(nonatomic, readonly) NSUInteger buttonEffect; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, readonly) CAShapeLayer *shape; - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; -- (void) drawViewWith:(NSRange)highlightedRange +- (void) drawViewWith:(NSArray *)candidateRanges + highlightedIndex:(NSInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange; + pagingRange:(NSRange)pagingRange + buttonEffect:(NSUInteger)buttonEffect; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -306,6 +312,8 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _textView.layoutManager.usesFontLeading = NO; _textView.layoutManager.typesetterBehavior = NSTypesetterBehavior_10_4; _defaultTheme = [[SquirrelTheme alloc] init]; + _candidatePathRefs = [NSMutableArray arrayWithCapacity:_candidateRanges.count]; + _pagingRects = [NSMutableArray arrayWithCapacity:2]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; @@ -328,14 +336,18 @@ - (NSRect)contentRectForRange:(NSRange)range { } // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWith:(NSRange)highlightedRange +- (void) drawViewWith:(NSArray*)candidateRanges + highlightedIndex:(NSInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange { - _highlightedRange = highlightedRange; + pagingRange:(NSRange)pagingRange + buttonEffect:(NSUInteger)buttonEffect { + _candidateRanges = candidateRanges; + _highlightedIndex = highlightedIndex; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; + _buttonEffect = buttonEffect; self.needsDisplay = YES; } @@ -533,6 +545,16 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB } } +CGMutablePathRef polygonPathForVertex(NSArray *vertex) { + CGMutablePathRef path = CGPathCreateMutable(); + CGPathMoveToPoint(path, NULL, vertex[3].pointValue.x, vertex[3].pointValue.y); + for (NSUInteger i = 0; i < vertex.count; i++){ + CGPathAddLineToPoint(path, NULL, vertex[i].pointValue.x, vertex[i].pointValue.y); + } + CGPathCloseSubpath(path); + return path; +} + // All draws happen here - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *backgroundPath; @@ -543,7 +565,9 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *highlightedPreeditPath2; NSBezierPath *preeditPath; NSBezierPath *pagingPath; - NSBezierPath *candidatePath; + NSBezierPath *candidatesPath; + NSBezierPath *pageUpPath; + NSBezierPath *pageDownPath; SquirrelTheme * theme = self.currentTheme; [NSBezierPath setDefaultLineWidth:0]; @@ -557,7 +581,7 @@ - (void)drawRect:(NSRect)dirtyRect { preeditRect.size.width = backgroundRect.size.width; preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace; preeditRect.origin = backgroundRect.origin; - if (_highlightedRange.length == 0) { + if (_highlightedIndex == NSNotFound) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace; } if (theme.preeditBackgroundColor != nil) { @@ -576,41 +600,91 @@ - (void)drawRect:(NSRect)dirtyRect { if (theme.preeditBackgroundColor != nil) { pagingPath = drawSmoothLines(rectVertex(pagingRect), 0, 0); } + NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; + pageUpRect.size.height += theme.edgeInset.height/2; + pageUpRect.size.width += theme.hilitedCornerRadius/2; + pageUpRect.origin.y += theme.edgeInset.height; + pageUpRect.origin.x += theme.edgeInset.width - theme.hilitedCornerRadius/2; + _pagingRects[0] = [NSValue valueWithRect:pageUpRect]; + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; + pageDownRect.size.height += theme.edgeInset.height/2; + pageDownRect.size.width += theme.hilitedCornerRadius/2; + pageDownRect.origin.y += theme.edgeInset.height; + pageDownRect.origin.x += theme.edgeInset.width; + _pagingRects[1] = [NSValue valueWithRect:pageDownRect]; + if (theme.highlightedPreeditColor != nil) { + pageUpPath = drawSmoothLines(rectVertex(pageUpRect), theme.edgeInset.width/4, theme.edgeInset.height/4); + pageDownPath = drawSmoothLines(rectVertex(pageDownRect), theme.edgeInset.width/4, theme.edgeInset.height/4); + } } // Draw candidate Rect - CGFloat halfLinespace = theme.linespace / 2; - NSRect candidateRect = backgroundRect; - candidateRect.size.height -= preeditRect.size.height + pagingRect.size.height; - candidateRect.origin.y += preeditRect.size.height; - if (_preeditRange.length == 0) { - candidateRect.size.height -= theme.edgeInset.height/2; - candidateRect.origin.y += theme.edgeInset.height/2; - } - - NSRect outerBox = NSInsetRect(candidateRect, theme.edgeInset.width/2, 0); - NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2+1, halfLinespace+1); + if (_highlightedIndex != NSNotFound) { + CGFloat halfLinespace = theme.linespace/2; + NSRect candidatesRect = backgroundRect; + candidatesRect.size.height -= preeditRect.size.height + pagingRect.size.height; + candidatesRect.origin.y += preeditRect.size.height; + if (_preeditRange.length == 0) { + candidatesRect.size.height -= theme.edgeInset.height/2; + candidatesRect.origin.y += theme.edgeInset.height/2; + } + if (_pagingRange.length == 0) { + candidatesRect.size.height -= theme.edgeInset.height/2; + } + NSRect outerBox = NSInsetRect(candidatesRect, theme.edgeInset.width/2, 0); + NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2+1, halfLinespace+1); - if (theme.preeditBackgroundColor != NULL) { - candidatePath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - } + if (theme.preeditBackgroundColor != NULL) { + candidatesPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + } - // Drawhighlighted Rect - if (_highlightedRange.length > 0 && theme.highlightedStripColor != nil) { if (theme.linear) { - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - - NSMutableArray *highlightedPoints; - NSMutableArray *highlightedPoints2; - // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + NSRange candidateRange = _candidateRanges[i].rangeValue; + NSRect leadingRect; + NSRect bodyRect; + NSRect trailingRect; + [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + + NSMutableArray *candidatePoints; + NSMutableArray *candidatePoints2; + // Handles the special case where containing boxes are separated + if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + candidatePoints = [rectVertex(leadingRect) copy]; + candidatePoints2 = [rectVertex(trailingRect) copy]; + } else { + candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) copy]; + } + + CGMutablePathRef candidatePathRef = polygonPathForVertex(candidatePoints); + if (candidatePoints2.count > 0) { + CGPathAddPath(candidatePathRef, NULL, polygonPathForVertex(candidatePoints2)); + } + _candidatePathRefs[i] = (__bridge NSValue * _Nonnull)(candidatePathRef); + + if (i == _highlightedIndex && theme.highlightedStripColor != nil) { + highlightedPath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + if (candidatePoints2.count > 0) { + highlightedPath2 = drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + } + } + } + } else { + for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + NSRange candidateRange = _candidateRanges[i].rangeValue; + NSRect candidateRect = [self contentRectForRange:candidateRange]; + candidateRect.size.width = textField.size.width; + candidateRect.origin.x = textField.origin.x; + candidateRect.origin.y += theme.edgeInset.height; + NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; + expand(candidatePoints, innerBox, outerBox); + + CGMutablePathRef candidatePathRef = polygonPathForVertex(candidatePoints); + _candidatePathRefs[i] = (__bridge NSValue * _Nonnull)(candidatePathRef); + + if (i == _highlightedIndex && theme.highlightedStripColor != nil) { + highlightedPath = drawSmoothLines(candidatePoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); + } } // xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); @@ -726,31 +800,46 @@ - (void)drawRect:(NSRect)dirtyRect { #endif if (theme.preeditBackgroundColor && - (_preeditRange.length > 0 || _highlightedRange.length > 0)) { + (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { [theme.preeditBackgroundColor setFill]; [backgroundPath fill]; - if (_highlightedRange.length > 0){ + if (_highlightedIndex != NSNotFound){ [[NSColor clearColor] setFill]; - [candidatePath fill]; + [candidatesPath fill]; [theme.backgroundColor setFill]; - [candidatePath fill]; + [candidatesPath fill]; } } else { [theme.backgroundColor setFill]; [backgroundPath fill]; } - if (theme.highlightedStripColor && ![highlightedPath isEmpty]) { + if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { [theme.highlightedStripColor setFill]; [highlightedPath fill]; if (![highlightedPath2 isEmpty]) { [highlightedPath2 fill]; } } - if (theme.highlightedPreeditColor && ![highlightedPreeditPath isEmpty]) { - [theme.highlightedPreeditColor setFill]; - [highlightedPreeditPath fill]; - if (![highlightedPreeditPath2 isEmpty]) { - [highlightedPreeditPath2 fill]; + if (theme.highlightedPreeditColor) { + if (_buttonEffect == NSPageUpFunctionKey) { + [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; + [pageUpPath fill]; + } if (_buttonEffect == NSBeginFunctionKey) { + [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; + [pageUpPath fill]; + } else if (_buttonEffect == NSPageDownFunctionKey) { + [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; + [pageDownPath fill]; + } else if (_buttonEffect == NSEndFunctionKey) { + [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; + [pageDownPath fill]; + } + if(![highlightedPreeditPath isEmpty]) { + [theme.highlightedPreeditColor setFill]; + [highlightedPreeditPath fill]; + if (![highlightedPreeditPath2 isEmpty]) { + [highlightedPreeditPath2 fill]; + } } } if (theme.borderColor && (theme.borderWidth > 0)) { @@ -763,17 +852,50 @@ - (void)drawRect:(NSRect)dirtyRect { } +- (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { + if (CGPathContainsPoint(_shape.path, nil, _point, NO)) { + NSPoint point = NSMakePoint(_point.x - self.textView.textContainerInset.width, + _point.y - self.textView.textContainerInset.height); + if (_pagingRects[0] != nil && NSMouseInRect(point, _pagingRects[0].rectValue, YES)) { + *_index = NSPageUpFunctionKey; + return YES; + } else if (_pagingRects[1] != nil && NSMouseInRect(point, _pagingRects[1].rectValue, YES)) { + *_index = NSPageDownFunctionKey; + return YES; + } + for (NSUInteger i = 0; i < _candidatePathRefs.count; i += 1) { + CGPathRef pathRef = (__bridge CGPathRef)([_candidatePathRefs objectAtIndex:i]); + if (CGPathContainsPoint(pathRef, NULL, point, NO)) { + *_index = i; + return YES; + } + } + } + return NO; +} + @end @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; - NSRange _preeditRange; NSRect _screenRect; CGFloat _maxHeight; CGFloat _maxTextWidth; + NSString *_preedit; + NSRange _selRange; + NSUInteger _caretPos; + NSArray *_candidates; + NSArray *_comments; + NSArray *_labels; + NSUInteger _index; + NSUInteger _pageNum; + NSInteger _buttonEffect; + BOOL _lastPage; + NSUInteger _cursorIndex; + NSString *_statusMessage; NSTimer *_statusTimer; } @@ -883,7 +1005,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:NSWindowStyleMaskBorderless + styleMask:NSWindowStyleMaskNonactivatingPanel backing:NSBackingStoreBuffered defer:YES]; if (self) { @@ -917,6 +1039,64 @@ - (instancetype)init { return self; } +- (NSPoint)mousePosition { + NSPoint point = NSEvent.mouseLocation; + point = [self convertPointFromScreen:point]; + return [_view convertPoint:point fromView:nil]; +} + +- (void)sendEvent:(NSEvent *)event { + switch (event.type) { + case NSEventTypeLeftMouseDown: { + NSPoint point = [self mousePosition]; + NSInteger index = NSNotFound; + if ([_view clickAtPoint:point index:&index]) { + if ((index >= 0 && index < _candidates.count) || + index == NSPageUpFunctionKey || index == NSPageDownFunctionKey) { + _index = index; + } + } + } break; + case NSEventTypeLeftMouseUp: { + NSPoint point = [self mousePosition]; + NSInteger index = NSNotFound; + if ([_view clickAtPoint: point index:&index]) { + if (((index >= 0 && index < _candidates.count) || index == NSPageUpFunctionKey || + index == NSPageDownFunctionKey) && index == _index) { + [_inputController selectCandidate:index]; + } + } + } break; + case NSEventTypeMouseEntered: { + self.acceptsMouseMovedEvents = YES; + } break; + case NSEventTypeMouseExited: { + self.acceptsMouseMovedEvents = NO; + if (_cursorIndex != _index) { + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels + highlighted:_index pageNum:_pageNum lastPage:_lastPage buttonEffect:NSNotFound update:NO]; + } + } break; + case NSEventTypeMouseMoved: { + NSPoint point = [self mousePosition]; + NSInteger index = NSNotFound; + if ([_view clickAtPoint: point index:&index]) { + if (index >= 0 && index < _candidates.count && _cursorIndex != index) { + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels + highlighted:index pageNum:_pageNum lastPage:_lastPage buttonEffect:NSNotFound update:NO]; + } else if (index == NSPageUpFunctionKey || index == NSPageDownFunctionKey || + index == NSBeginFunctionKey || index == NSEndFunctionKey) { + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels + highlighted:_index pageNum:_pageNum lastPage:_lastPage buttonEffect:index update:NO]; + } + } + } break; + default: + break; + } + [super sendEvent:event]; +} + - (void)getCurrentScreen { // get current screen _screenRect = [NSScreen mainScreen].frame; @@ -983,8 +1163,8 @@ - (void)show { } // Make the first candidate fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); - if (!sweepVertical && _preeditRange.length > 0) { - NSSize preeditSize = [_view contentRectForRange:_preeditRange].size; + if (!sweepVertical && _view.preeditRange.length > 0) { + NSSize preeditSize = [_view contentRectForRange:_view.preeditRange].size; windowRect.origin.x += preeditSize.height + theme.edgeInset.width / 2; } } else { @@ -1060,7 +1240,29 @@ - (void)showPreedit:(NSString *)preedit labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage { + lastPage:(BOOL)lastPage + buttonEffect:(NSUInteger)buttonEffect + update:(BOOL)update { + if (update) { + _preedit = preedit; + _selRange = selRange; + _caretPos = caretPos; + _candidates = candidates; + _comments = comments; + _labels = labels; + _index = index; + _pageNum = pageNum; + _lastPage = lastPage; + } + _cursorIndex = index; + if (buttonEffect == NSPageUpFunctionKey || buttonEffect == NSBeginFunctionKey) { + _buttonEffect = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; + } else if (buttonEffect == NSPageDownFunctionKey || buttonEffect == NSEndFunctionKey) { + _buttonEffect = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + } else { + _buttonEffect = NSNotFound; + } + NSUInteger numCandidates = candidates.count; if (numCandidates || (preedit && preedit.length)) { _statusMessage = nil; @@ -1082,7 +1284,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSUInteger candidateStartPos = 0; - _preeditRange = NSMakeRange(NSNotFound, 0); + NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); // preedit if (preedit) { @@ -1110,9 +1312,9 @@ - (void)showPreedit:(NSString *)preedit [preeditLine addAttribute:NSParagraphStyleAttributeName value:paragraphStylePreedit range:NSMakeRange(0, preeditLine.length)]; - + [text appendAttributedString:preeditLine]; - _preeditRange = NSMakeRange(0, text.length); + preeditRange = NSMakeRange(0, text.length); if (numCandidates) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; @@ -1121,9 +1323,9 @@ - (void)showPreedit:(NSString *)preedit } // candidates + NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); CGFloat candidateTextWidth = 0 - separatorWidth; - NSRange highlightedRange = NSMakeRange(NSNotFound, 0); NSUInteger i; for (i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; @@ -1212,9 +1414,7 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate.headIndent = labelWidth; } [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; - if (i == index) { - highlightedRange = NSMakeRange(text.length, line.length); - } + [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length, line.length)]]; [text appendAttributedString:line]; } @@ -1228,14 +1428,14 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; - [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(pageNum ? @"◀" : @"◁") - attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀" : @"◁") + attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] - attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] + attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSMutableAttributedString alloc] initWithString:(lastPage ? @"▷" : @"▶") - attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷" : @"▶") + attributes:theme.pagingAttrs]]; fixDefaultFont(paging); NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; @@ -1251,7 +1451,7 @@ - (void)showPreedit:(NSString *)preedit _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; // text done! - [_view drawViewWith:highlightedRange preeditRange:_preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange]; + [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange buttonEffect:_buttonEffect]; [self show]; } @@ -1284,7 +1484,7 @@ - (void)showStatus:(NSString *)message { _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:emptyRange preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange]; + [_view drawViewWith:@[] highlightedIndex:NSNotFound preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange buttonEffect:NSNotFound]; [self show]; if (_statusTimer) { @@ -1657,6 +1857,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; pagingParagraphStyle.alignment = NSTextAlignmentRight; + pagingParagraphStyle.tailIndent = -hilitedCornerRadius/2; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols From 5e7ae1216643c6950797f18d4a1983c3404e0e4f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 22 Apr 2023 19:47:31 +0200 Subject: [PATCH 051/164] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index d929602d4..e5b27868b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ lib/* data/opencc/ data/plum/ download/ +Frameworks/ package/Squirrel.pkg package/sign/*.pem package/test-* From bc8d9b58e7167142b853a0ed1c00052747eaf6ae Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 22 Apr 2023 17:00:50 +0200 Subject: [PATCH 052/164] line height correction --- .gitmodules | 4 ++++ SquirrelPanel.m | 40 +++------------------------------------- 2 files changed, 7 insertions(+), 37 deletions(-) diff --git a/.gitmodules b/.gitmodules index b996b66d2..5317921e7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,7 @@ path = plum url = https://github.com/groverlynn/plum.git branch = test +[submodule "Sparkle"] + path = Sparkle + url = https://github.com/sparkle-project/Sparkle + branch = master \ No newline at end of file diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d592274bc..8a469e8ab 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -476,7 +476,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe lastLineRect.size.width = textContainer.size.width - lastLineRect.origin.x; NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; - while (lastLineRange.length>0 && (glyphProperty & NSGlyphPropertyElastic && glyphProperty & NSGlyphPropertyControlCharacter)) { + while (lastLineRange.length>0 && (glyphProperty & NSGlyphPropertyElastic & NSGlyphPropertyControlCharacter)) { lastLineRange.length -= 1; glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; } @@ -686,38 +686,6 @@ - (void)drawRect:(NSRect)dirtyRect { highlightedPath = drawSmoothLines(candidatePoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); } } - -// xyTranslation(highlightedPoints, NSMakePoint(0, -halfLinespace)); -// xyTranslation(highlightedPoints2, NSMakePoint(0, -halfLinespace)); -// innerBox.size.height -= halfLinespace; - // Expand the boxes to reach proper border -// expand(highlightedPoints, innerBox, outerBox); -// expand(highlightedPoints2, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - if (highlightedPoints2.count > 0) { - highlightedPath2 = drawSmoothLines(highlightedPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - } - } else { - NSRect highlightedRect = [self contentRectForRange:_highlightedRange]; - highlightedRect.size.width = textField.size.width; -// highlightedRect.size.height += halfLinespace; - highlightedRect.origin.x = textField.origin.x; - highlightedRect.origin.y += theme.edgeInset.height; - // if (_highlightedRange.location+_highlightedRange.length == _text.length) { - // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - // } - // if (_highlightedRange.location - ((_preeditRange.location == NSNotFound ? 0 : _preeditRange.location)+_preeditRange.length) <= 1) { - // if (_preeditRange.length == 0) { - // highlightedRect.size.height += theme.edgeInset.height - halfLinespace; - // highlightedRect.origin.y -= theme.edgeInset.height - halfLinespace; - // } else { - // highlightedRect.size.height += theme.hilitedCornerRadius / 2; - // highlightedRect.origin.y -= theme.hilitedCornerRadius / 2; - // } - // } - NSMutableArray *highlightedPoints = [rectVertex(highlightedRect) mutableCopy]; - expand(highlightedPoints, innerBox, outerBox); - highlightedPath = drawSmoothLines(highlightedPoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); } } @@ -730,7 +698,7 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect outerBox = preeditRect; outerBox.size.width -= theme.edgeInset.width; - outerBox.size.height -= theme.edgeInset.height/2 + theme.hilitedCornerRadius/2; + outerBox.size.height -= theme.edgeInset.height/2 + + theme.preeditLinespace - theme.hilitedCornerRadius/2; outerBox.origin.x += theme.edgeInset.width/2; outerBox.origin.y += theme.edgeInset.height/2; NSRect innerBox = preeditRect; @@ -1417,14 +1385,12 @@ - (void)showPreedit:(NSString *)preedit [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length, line.length)]]; [text appendAttributedString:line]; } - + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates && theme.showPaging) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; From 314ddd50335fe64e6bc9cafc93953578f54ea336 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 23 Apr 2023 22:59:27 +0200 Subject: [PATCH 053/164] misc --- SquirrelPanel.m | 9 +-------- input_source.m | 6 +++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 8a469e8ab..00674c5bd 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -584,9 +584,6 @@ - (void)drawRect:(NSRect)dirtyRect { if (_highlightedIndex == NSNotFound) { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace; } - if (theme.preeditBackgroundColor != nil) { - preeditPath = drawSmoothLines(rectVertex(preeditRect), 0, 0); - } } // Draw paging Rect @@ -597,9 +594,7 @@ - (void)drawRect:(NSRect)dirtyRect { pagingRect.size.height += theme.edgeInset.height; pagingRect.origin.x = backgroundRect.origin.x; pagingRect.origin.y += theme.edgeInset.height; - if (theme.preeditBackgroundColor != nil) { - pagingPath = drawSmoothLines(rectVertex(pagingRect), 0, 0); - } + NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; pageUpRect.size.height += theme.edgeInset.height/2; pageUpRect.size.width += theme.hilitedCornerRadius/2; @@ -1251,7 +1246,6 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; - NSUInteger candidateStartPos = 0; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); // preedit @@ -1287,7 +1281,6 @@ - (void)showPreedit:(NSString *)preedit [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } - candidateStartPos = text.length; } // candidates diff --git a/input_source.m b/input_source.m index 2f8cedd12..e649e5f62 100644 --- a/input_source.m +++ b/input_source.m @@ -14,7 +14,7 @@ #define HANT_INPUT_MODE (1 << 1) #define CANT_INPUT_MODE (1 << 2) -void RegisterInputSource() { +void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); if (installedLocationURL) { @@ -51,7 +51,7 @@ void ActivateInputSource(int enabled_modes) { CFRelease(sourceList); } -void DeactivateInputSource() { +void DeactivateInputSource(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( @@ -73,7 +73,7 @@ void DeactivateInputSource() { CFRelease(sourceList); } -int GetEnabledInputModes() { +int GetEnabledInputModes(void) { int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { From 3a252b9665dc5704bc17eb12d8abb2776ebde51c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 26 Apr 2023 17:11:48 +0200 Subject: [PATCH 054/164] Fix highlight with combined glyphs --- SquirrelInputController.m | 2 +- SquirrelPanel.m | 216 ++++++++++++++------------------------ 2 files changed, 79 insertions(+), 139 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index eaaf10275..99f29120b 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -548,7 +548,7 @@ -(void)rimeConsumeCommittedText char substring[length+1]; strncpy(substring, str, length); substring[length] = '\0'; - return [NSString stringWithCString:substring encoding:NSUTF8StringEncoding]; + return [NSString stringWithUTF8String:substring]; } -(void)rimeUpdate diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 00674c5bd..4d5cf4f52 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -438,62 +438,27 @@ BOOL nearEmptyRect(NSRect rect) { - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { NSLayoutManager *layoutManager = _textView.layoutManager; NSTextContainer *textContainer = _textView.textContainer; - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange fullRangeInBoundingRect = [layoutManager glyphRangeForBoundingRect:boundingRect inTextContainer:textContainer]; + NSSize edgeInset = self.currentTheme.edgeInset; *leadingRect = NSZeroRect; - *bodyRect = boundingRect; + *bodyRect = NSZeroRect; *trailingRect = NSZeroRect; - if (boundingRect.origin.x <= 1 && fullRangeInBoundingRect.location < glyphRange.location) { - *leadingRect = [layoutManager boundingRectForGlyphRange:NSMakeRange(fullRangeInBoundingRect.location, glyphRange.location-fullRangeInBoundingRect.location) inTextContainer:textContainer]; - if (!nearEmptyRect(*leadingRect)) { - bodyRect->size.height -= leadingRect->size.height; - bodyRect->origin.y += leadingRect->size.height; - } - double rightEdge = NSMaxX(*leadingRect); - leadingRect->origin.x = rightEdge; - leadingRect->size.width = bodyRect->origin.x + bodyRect->size.width - rightEdge; - } - if (NSMaxRange(fullRangeInBoundingRect) > NSMaxRange(glyphRange)) { - *trailingRect = [layoutManager boundingRectForGlyphRange: - NSMakeRange(NSMaxRange(glyphRange), NSMaxRange(fullRangeInBoundingRect)-NSMaxRange(glyphRange)) - inTextContainer:textContainer]; - if (!nearEmptyRect(*trailingRect)) { - bodyRect->size.height -= trailingRect->size.height; - } - double leftEdge = NSMinX(*trailingRect); - trailingRect->origin.x = bodyRect->origin.x; - trailingRect->size.width = leftEdge - bodyRect->origin.x; - } else if (NSMaxRange(fullRangeInBoundingRect) == NSMaxRange(glyphRange)) { - *trailingRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; - if (NSMaxX(*trailingRect) >= NSMaxX(boundingRect) - 1) { - *trailingRect = NSZeroRect; - } else if (!nearEmptyRect(*trailingRect)) { - bodyRect->size.height -= trailingRect->size.height; - } - } - NSRect lastLineRect = nearEmptyRect(*trailingRect) ? *bodyRect : *trailingRect; - lastLineRect.size.width = textContainer.size.width - lastLineRect.origin.x; - NSRange lastLineRange = [layoutManager glyphRangeForBoundingRect:lastLineRect inTextContainer:textContainer]; - NSGlyphProperty glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; - while (lastLineRange.length>0 && (glyphProperty & NSGlyphPropertyElastic & NSGlyphPropertyControlCharacter)) { - lastLineRange.length -= 1; - glyphProperty = [layoutManager propertyForGlyphAtIndex:NSMaxRange(lastLineRange)-1]; - } - if (NSMaxRange(lastLineRange) == NSMaxRange(glyphRange)) { - if (!nearEmptyRect(*trailingRect)) { - *trailingRect = lastLineRect; - } else { - *bodyRect = lastLineRect; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; + NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; + NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); + NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; + if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { + *bodyRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, endPoint.x-startPoint.x, NSHeight(leadingLineRect)); + } else { + *leadingRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, NSWidth(leadingLineRect)-startPoint.x, NSHeight(leadingLineRect)); + NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); + NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; + *trailingRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMinY(trailingLineRect)+edgeInset.height, endPoint.x, NSHeight(trailingLineRect)); + NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); + if (bodyLineRange.length > 0) { + *bodyRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMaxY(leadingLineRect)+edgeInset.height, NSMaxX(leadingLineRect)-NSMinX(trailingLineRect), NSMinY(trailingLineRect)-NSMaxY(leadingLineRect)); } } - NSSize edgeInset = self.currentTheme.edgeInset; - leadingRect->origin.x += edgeInset.width; - leadingRect->origin.y += edgeInset.height; - bodyRect->origin.x += edgeInset.width; - bodyRect->origin.y += edgeInset.height; - trailingRect->origin.x += edgeInset.width; - trailingRect->origin.y += edgeInset.height; } // Based on the 3 boxes from multilineRectForRange, calculate the vertex of the polygon containing the text in range @@ -507,20 +472,20 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], bodyVertex[1]]; + return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { NSArray * trailingVertex = rectVertex(trailingRect); NSArray * bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], bodyVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[trailingVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], leadingVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[leadingVertex[0], leadingVertex[1], trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { NSArray * leadingVertex = rectVertex(leadingRect); NSArray * bodyVertex = rectVertex(bodyRect); NSArray * trailingVertex = rectVertex(trailingRect); - return @[bodyVertex[0], leadingVertex[1], leadingVertex[0], leadingVertex[3], bodyVertex[2], trailingVertex[3], trailingVertex[2], trailingVertex[1]]; + return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3]]; } else { return @[]; } @@ -547,8 +512,8 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB CGMutablePathRef polygonPathForVertex(NSArray *vertex) { CGMutablePathRef path = CGPathCreateMutable(); - CGPathMoveToPoint(path, NULL, vertex[3].pointValue.x, vertex[3].pointValue.y); - for (NSUInteger i = 0; i < vertex.count; i++){ + CGPathMoveToPoint(path, NULL, vertex[vertex.count-1].pointValue.x, vertex[vertex.count-1].pointValue.y); + for (NSUInteger i = 0; i < vertex.count; i += 1){ CGPathAddLineToPoint(path, NULL, vertex[i].pointValue.x, vertex[i].pointValue.y); } CGPathCloseSubpath(path); @@ -563,8 +528,7 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *highlightedPath2; NSBezierPath *highlightedPreeditPath; NSBezierPath *highlightedPreeditPath2; - NSBezierPath *preeditPath; - NSBezierPath *pagingPath; + NSBezierPath *nonCandidatesPath; NSBezierPath *candidatesPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; @@ -579,10 +543,10 @@ - (void)drawRect:(NSRect)dirtyRect { if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; preeditRect.size.width = backgroundRect.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace; + preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2; preeditRect.origin = backgroundRect.origin; if (_highlightedIndex == NSNotFound) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace; + preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; } } @@ -595,41 +559,34 @@ - (void)drawRect:(NSRect)dirtyRect { pagingRect.origin.x = backgroundRect.origin.x; pagingRect.origin.y += theme.edgeInset.height; - NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; - pageUpRect.size.height += theme.edgeInset.height/2; - pageUpRect.size.width += theme.hilitedCornerRadius/2; + NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 2)]; pageUpRect.origin.y += theme.edgeInset.height; - pageUpRect.origin.x += theme.edgeInset.width - theme.hilitedCornerRadius/2; + pageUpRect.origin.x += theme.edgeInset.width; _pagingRects[0] = [NSValue valueWithRect:pageUpRect]; - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; - pageDownRect.size.height += theme.edgeInset.height/2; - pageDownRect.size.width += theme.hilitedCornerRadius/2; + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-2, 2)]; pageDownRect.origin.y += theme.edgeInset.height; pageDownRect.origin.x += theme.edgeInset.width; _pagingRects[1] = [NSValue valueWithRect:pageDownRect]; if (theme.highlightedPreeditColor != nil) { - pageUpPath = drawSmoothLines(rectVertex(pageUpRect), theme.edgeInset.width/4, theme.edgeInset.height/4); - pageDownPath = drawSmoothLines(rectVertex(pageDownRect), theme.edgeInset.width/4, theme.edgeInset.height/4); + pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*pageUpRect.size.height, 0.28*pageUpRect.size.height); + pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*pageDownRect.size.height, 0.28*pageDownRect.size.height); } } // Draw candidate Rect if (_highlightedIndex != NSNotFound) { - CGFloat halfLinespace = theme.linespace/2; - NSRect candidatesRect = backgroundRect; - candidatesRect.size.height -= preeditRect.size.height + pagingRect.size.height; - candidatesRect.origin.y += preeditRect.size.height; + NSRect outerBox = NSInsetRect(backgroundRect, theme.edgeInset.width, 0); + outerBox.size.height -= preeditRect.size.height + pagingRect.size.height; + outerBox.origin.y += preeditRect.size.height; if (_preeditRange.length == 0) { - candidatesRect.size.height -= theme.edgeInset.height/2; - candidatesRect.origin.y += theme.edgeInset.height/2; + outerBox.size.height -= theme.edgeInset.height; + outerBox.origin.y += theme.edgeInset.height; } if (_pagingRange.length == 0) { - candidatesRect.size.height -= theme.edgeInset.height/2; + outerBox.size.height -= theme.edgeInset.height; } - NSRect outerBox = NSInsetRect(candidatesRect, theme.edgeInset.width/2, 0); - NSRect innerBox = NSInsetRect(outerBox, theme.edgeInset.width/2+1, halfLinespace+1); - - if (theme.preeditBackgroundColor != NULL) { + NSRect innerBox = NSInsetRect(outerBox, 1, theme.linespace/2+1); + if (theme.preeditBackgroundColor != nil) { candidatesPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } @@ -678,7 +635,7 @@ - (void)drawRect:(NSRect)dirtyRect { _candidatePathRefs[i] = (__bridge NSValue * _Nonnull)(candidatePathRef); if (i == _highlightedIndex && theme.highlightedStripColor != nil) { - highlightedPath = drawSmoothLines(candidatePoints, theme.hilitedCornerRadius*0.3, theme.hilitedCornerRadius*1.4); + highlightedPath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } } } @@ -691,17 +648,6 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - NSRect outerBox = preeditRect; - outerBox.size.width -= theme.edgeInset.width; - outerBox.size.height -= theme.edgeInset.height/2 + + theme.preeditLinespace - theme.hilitedCornerRadius/2; - outerBox.origin.x += theme.edgeInset.width/2; - outerBox.origin.y += theme.edgeInset.height/2; - NSRect innerBox = preeditRect; - innerBox.size.width -= theme.edgeInset.width * 2 + 2; - innerBox.size.height -= theme.edgeInset.height * 2 + theme.preeditLinespace; - innerBox.origin.x += theme.edgeInset.width + 1; - innerBox.origin.y += theme.edgeInset.height + 1; - NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated @@ -711,9 +657,6 @@ - (void)drawRect:(NSRect)dirtyRect { } else { highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - // Expand the boxes to reach proper border - expand(highlightedPreeditPoints, innerBox, outerBox); - expand(highlightedPreeditPoints2, innerBox, outerBox); highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); @@ -762,19 +705,17 @@ - (void)drawRect:(NSRect)dirtyRect { [preeditPath setWindingRule:NSEvenOddWindingRule]; #endif + [theme.backgroundColor setFill]; + [backgroundPath fill]; if (theme.preeditBackgroundColor && (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { - [theme.preeditBackgroundColor setFill]; - [backgroundPath fill]; - if (_highlightedIndex != NSNotFound){ - [[NSColor clearColor] setFill]; - [candidatesPath fill]; - [theme.backgroundColor setFill]; - [candidatesPath fill]; + nonCandidatesPath = [backgroundPath copy]; + if (![candidatesPath isEmpty]) { + [nonCandidatesPath appendBezierPath:candidatesPath]; } - } else { - [theme.backgroundColor setFill]; - [backgroundPath fill]; + [nonCandidatesPath setWindingRule:NSEvenOddWindingRule]; + [theme.preeditBackgroundColor setFill]; + [nonCandidatesPath fill]; } if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { [theme.highlightedStripColor setFill]; @@ -819,10 +760,10 @@ - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { if (CGPathContainsPoint(_shape.path, nil, _point, NO)) { NSPoint point = NSMakePoint(_point.x - self.textView.textContainerInset.width, _point.y - self.textView.textContainerInset.height); - if (_pagingRects[0] != nil && NSMouseInRect(point, _pagingRects[0].rectValue, YES)) { + if (_pagingRects[0] != nil && NSPointInRect(point, _pagingRects[0].rectValue)) { *_index = NSPageUpFunctionKey; return YES; - } else if (_pagingRects[1] != nil && NSMouseInRect(point, _pagingRects[1].rectValue, YES)) { + } else if (_pagingRects[1] != nil && NSPointInRect(point, _pagingRects[1].rectValue)) { *_index = NSPageDownFunctionKey; return YES; } @@ -1088,6 +1029,7 @@ - (void)show { } //Break line if the text is too long, based on screen size. + CGFloat textWidth = _view.textView.textStorage.size.width; NSFont *currentFont = theme.attrs[NSFontAttributeName]; CGFloat fontScale = currentFont.pointSize / 12; @@ -1099,10 +1041,12 @@ - (void)show { textWidth = _maxTextWidth; } _view.textView.textContainer.containerSize = NSMakeSize(textWidth, 0); + NSRect contentRect = _view.contentRect; + if (contentRect.size.width > textWidth) { + contentRect.size.width = textWidth; + } - NSRect windowRect; // in vertical mode, the width and height are interchanged - NSRect contentRect = _view.contentRect; if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { @@ -1114,6 +1058,7 @@ - (void)show { } // the sweep direction of the client app changes the behavior of adjusting squirrel panel position + NSRect windowRect; bool sweepVertical = NSWidth(_position) > NSHeight(_position); if (theme.vertical) { windowRect.size = NSMakeSize(contentRect.size.height + theme.edgeInset.height * 2, @@ -1217,7 +1162,12 @@ - (void)showPreedit:(NSString *)preedit _pageNum = pageNum; _lastPage = lastPage; } + NSUInteger numCandidates = candidates.count; + if (numCandidates == 0) { + _index = index = NSNotFound; + } _cursorIndex = index; + if (buttonEffect == NSPageUpFunctionKey || buttonEffect == NSBeginFunctionKey) { _buttonEffect = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; } else if (buttonEffect == NSPageDownFunctionKey || buttonEffect == NSEndFunctionKey) { @@ -1226,7 +1176,6 @@ - (void)showPreedit:(NSString *)preedit _buttonEffect = NSNotFound; } - NSUInteger numCandidates = candidates.count; if (numCandidates || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { @@ -1270,9 +1219,8 @@ - (void)showPreedit:(NSString *)preedit } fixDefaultFont(preeditLine); - NSMutableParagraphStyle *paragraphStylePreedit = [theme.preeditParagraphStyle mutableCopy]; [preeditLine addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit + value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; [text appendAttributedString:preeditLine]; @@ -1285,7 +1233,7 @@ - (void)showPreedit:(NSString *)preedit // candidates NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; - CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:(theme.linear ? @" " : @"\n") attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:@" " attributes:theme.labelAttrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); CGFloat candidateTextWidth = 0 - separatorWidth; NSUInteger i; for (i = 0; i < candidates.count; ++i) { @@ -1349,10 +1297,11 @@ - (void)showPreedit:(NSString *)preedit suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString - attributes:attrs]]; + attributes:labelAttrs]]; } fixDefaultFont(line); + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; // determine if the line is too wide and line break is needed, based on screen size. NSString *separtatorString = @"\u2029"; if (theme.linear) { @@ -1364,21 +1313,22 @@ - (void)showPreedit:(NSString *)preedit } else { separtatorString = @" "; } + } else { + paragraphStyleCandidate.headIndent = labelWidth; } - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:attrs]; + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:theme.labelAttrs]; - NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (i > 0) { [text appendAttributedString:separator]; } - if (!theme.linear) { - paragraphStyleCandidate.headIndent = labelWidth; - } - [line addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, line.length)]; + [line addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleCandidate + range:NSMakeRange(0, line.length)]; [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length, line.length)]]; [text appendAttributedString:line]; } - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.labelAttrs]]; + // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); @@ -1397,15 +1347,15 @@ - (void)showPreedit:(NSString *)preedit attributes:theme.pagingAttrs]]; fixDefaultFont(paging); - NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; [paging addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePaging + value:theme.pagingParagraphStyle range:NSMakeRange(0, paging.length)]; pagingRange = NSMakeRange(text.length, paging.length); [text appendAttributedString:paging]; } + [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1439,6 +1389,7 @@ - (void)showStatus:(NSString *)message { NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; fixDefaultFont(text); + [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1764,24 +1715,14 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } NSFontDescriptor *labelFontDescriptor = nil; NSFont *labelFont = nil; - NSMutableDictionary *labelFontAttr; - labelFontAttr[NSFontFixedAdvanceAttribute] = @(labelFontSize); - labelFontAttr[NSFontSizeAttribute] = @(labelFontSize); if (labelFontName != nil) { labelFontDescriptor = getFontDescriptor(labelFontName); - if (labelFontDescriptor == nil) { - labelFontDescriptor = [fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr]; - } if (labelFontDescriptor != nil) { labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; } } if (labelFont == nil) { - if (fontDescriptor != nil) { - labelFont = [NSFont fontWithDescriptor:[fontDescriptor fontDescriptorByAddingAttributes:labelFontAttr] size:labelFontSize]; - } else { labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; - } } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1805,18 +1746,17 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat lineHeight = font.ascender - font.descender; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.alignment = NSTextAlignmentJustified; + paragraphStyle.alignment = NSTextAlignmentLeft; paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2; paragraphStyle.lineSpacing = lineSpacing/2; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; preeditParagraphStyle.minimumLineHeight = lineHeight; - preeditParagraphStyle.paragraphSpacing = spacing; + preeditParagraphStyle.paragraphSpacing = spacing/2; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; pagingParagraphStyle.alignment = NSTextAlignmentRight; - pagingParagraphStyle.tailIndent = -hilitedCornerRadius/2; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols From 0c30474b173db1270dff80991c0baf823d39b3c9 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 2 May 2023 18:21:48 +0200 Subject: [PATCH 055/164] detect coalesced modifier --- SquirrelInputController.m | 48 ++++++++++----------------------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 99f29120b..54b2fa9e2 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -25,6 +25,7 @@ @implementation SquirrelInputController { NSArray *_candidates; NSUInteger _lastModifier; NSEventType _lastEventType; + uint32_t _lastEventCount; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; @@ -52,7 +53,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender _currentClient = sender; - NSUInteger modifiers = event.modifierFlags; + CGEventFlags modifiers = CGEventGetFlags(event.CGEvent); BOOL handled = NO; @@ -78,66 +79,41 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - int rime_keycode = 0; - // For flags-changed event, keyCode is available since macOS 10.15 (#715) - Bool keyCodeAvailable = NO; - if (@available(macOS 10.15, *)) { - keyCodeAvailable = YES; - rime_keycode = osx_keycode_to_rime_keycode(event.keyCode, 0, 0, 0); - //NSLog(@"keyCode: %d", event.keyCode); - } int release_mask = 0; NSUInteger changes = _lastModifier ^ modifiers; + int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + int64_t keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); + int rime_keycode = osx_keycode_to_rime_keycode((int)keyCode, 0, 0, 0); _lastModifier = modifiers; + uint32_t eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType); if (changes & OSX_CAPITAL_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Caps_Lock; - } - // NOTE: rime assumes XK_Caps_Lock to be sent before modifier changes, - // while NSFlagsChanged event has the flag changed already. - // so it is necessary to revert kLockMask. rime_modifiers ^= kLockMask; [self processKey:rime_keycode modifiers:rime_modifiers]; } if (changes & OSX_SHIFT_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Shift_L; - } - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask; + release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_CTRL_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Control_L; - } - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask; + release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_ALT_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Alt_L; - } - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask; + release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_FN_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Hyper_L; - } - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask; + release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_COMMAND_MASK) { - if (!keyCodeAvailable) { - rime_keycode = XK_Super_L; - } - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask; + release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; // do not update UI when using Command key break; } [self rimeUpdate]; + _lastEventCount = eventCount; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. From e09f857b7f8d858915223fca672e8a1af35c7fa1 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 2 May 2023 18:51:11 +0200 Subject: [PATCH 056/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index aa6c46bee..2dc90787f 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit aa6c46bee76d919d068cdf0974c27218c669974e +Subproject commit 2dc90787f5d9d5e90d91d99ab2212b85f34537da From c44b0e576ddd6adf7b29be08d88d52c02c8dc0ed Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 3 May 2023 22:21:13 +0200 Subject: [PATCH 057/164] background image --- SquirrelConfig.h | 2 ++ SquirrelConfig.m | 27 ++++++++++++++++++++++++++- SquirrelPanel.m | 7 +++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/SquirrelConfig.h b/SquirrelConfig.h index c4bd50145..ff397ed67 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -26,6 +26,8 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (NSString *)getString:(NSString *)option; // 0xaabbggrr or 0xbbggrr - (NSColor *)getColor:(NSString *)option; +// file path (absolute or relative to ~/Library/Rime) +- (NSColor *)getImage:(NSString *)option; - (SquirrelAppOptions *)getAppOptions:(NSString *)appName; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index d816f1b40..efd9cd3db 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -97,7 +97,6 @@ - (NSNumber *)getOptionalInt:(NSString *)option { return _cache[option] = @(value); } return [_baseConfig getOptionalInt:option]; - } - (NSNumber *)getOptionalDouble:(NSString *)option { @@ -138,6 +137,19 @@ - (NSColor *)getColor:(NSString *)option { return [_baseConfig getColor:option]; } +- (NSColor *)getImage:(NSString *)option { + NSColor *cachedValue = [self cachedValueOfClass:[NSColor class] forKey:option]; + if (cachedValue) { + return cachedValue; + } + NSColor *color = [self colorFromImage:[self getString:option]]; + if (color) { + _cache[option] = color; + return color; + } + return [_baseConfig getImage:option]; +} + - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { NSString * rootKey = [@"app_options/" stringByAppendingString:appName]; SquirrelMutableAppOptions* appOptions = [[SquirrelMutableAppOptions alloc] init]; @@ -188,4 +200,17 @@ - (NSColor *)colorFromString:(NSString *)string { } } +- (NSColor *)colorFromImage:(NSString *)image { + if (image == nil) { + return nil; + } + NSFileManager* fileManager = [NSFileManager defaultManager]; + [fileManager changeCurrentDirectoryPath:[@"~/Library/Rime" stringByStandardizingPath]]; + NSString *imageFile = [image stringByStandardizingPath]; + if ([fileManager fileExistsAtPath:imageFile]) { + NSColor *color = [NSColor colorWithPatternImage:[[NSImage alloc] initWithContentsOfFile:imageFile]]; + return color; + } + return nil; +} @end diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 4d5cf4f52..70dd1c3c5 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -60,6 +60,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, assign) BOOL native; @property(nonatomic, strong, readonly) NSColor *backgroundColor; +@property(nonatomic, strong, readonly) NSColor *backgroundImage; @property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @@ -100,6 +101,7 @@ - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setStatusMessageType:(NSString *)statusMessageType; - (void)setBackgroundColor:(NSColor *)backgroundColor + backgroundImage:(NSColor *)backgroundImage highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor @@ -172,11 +174,13 @@ - (void)setStatusMessageType:(NSString *)type { } - (void)setBackgroundColor:(NSColor *)backgroundColor + backgroundImage:(NSColor *)backgroundImage highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor { _backgroundColor = backgroundColor; + _backgroundImage = backgroundImage; _highlightedStripColor = highlightedStripColor; _highlightedPreeditColor = highlightedPreeditColor; _preeditBackgroundColor = preeditBackgroundColor; @@ -1513,6 +1517,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat baseOffset = [config getDouble:@"style/base_offset"]; NSColor *backgroundColor; + NSColor *backgroundImage; NSColor *borderColor; NSColor *preeditBackgroundColor; NSColor *candidateLabelColor; @@ -1540,6 +1545,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]]; } backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]]; + backgroundImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]]; borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]]; preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]]; textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]]; @@ -1849,6 +1855,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo pagingParagraphStyle:pagingParagraphStyle]; [theme setBackgroundColor:backgroundColor + backgroundImage:backgroundImage highlightedStripColor:highlightedCandidateBackColor highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor From cefac16588fc7ba71b8043b803c729ffa769a46b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 00:38:45 +0200 Subject: [PATCH 058/164] right click to delete candidate --- SquirrelInputController.h | 2 +- SquirrelInputController.m | 8 ++- SquirrelPanel.h | 2 +- SquirrelPanel.m | 125 +++++++++++++++++++++++--------------- 4 files changed, 84 insertions(+), 53 deletions(-) diff --git a/SquirrelInputController.h b/SquirrelInputController.h index eee4a33d7..da3aa21a5 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -2,5 +2,5 @@ #import @interface SquirrelInputController : IMKInputController -- (BOOL)selectCandidate:(NSInteger)index; +- (BOOL)actionWithCandidate:(NSInteger)index; @end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 54b2fa9e2..2253ca1fc 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -206,14 +206,16 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers return handled; } -- (BOOL)selectCandidate:(NSInteger)index { +- (BOOL)actionWithCandidate:(NSInteger)index { BOOL handled = NO; if (index == NSPageUpFunctionKey) { handled = rime_get_api()->process_key(_session, XK_Page_Up, 0); } else if (index == NSPageDownFunctionKey) { handled = rime_get_api()->process_key(_session, XK_Page_Down, 0); } else if (index >= 0 && index < 10) { - handled = rime_get_api()->select_candidate_on_current_page(_session, (int) index); + handled = rime_get_api()->select_candidate_on_current_page(_session, (int)index); + } else if (index >= -10 && index <= -1) { // -1-index for deletion + handled = rime_get_api()->delete_candidate_on_current_page(_session, (int)-1-index); } if (handled) { [self rimeUpdate]; @@ -462,7 +464,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit highlighted:index pageNum:pageNum lastPage:lastPage - buttonEffect:NSNotFound + turnPage:NSNotFound update:YES]; } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index aea58c5b8..ea64327dc 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -27,7 +27,7 @@ highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage - buttonEffect:(NSUInteger)buttonEffect + turnPage:(NSUInteger)turnPage update:(BOOL)update; -(void)hide; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 70dd1c3c5..a9e024fb4 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -187,13 +187,13 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor _borderColor = borderColor; } -- (void)setCornerRadius:(double)cornerRadius - hilitedCornerRadius:(double)hilitedCornerRadius +- (void)setCornerRadius:(CGFloat)cornerRadius + hilitedCornerRadius:(CGFloat)hilitedCornerRadius edgeInset:(NSSize)edgeInset - borderWidth:(double)borderWidth - linespace:(double)linespace - preeditLinespace:(double)preeditLinespace - alpha:(double)alpha + borderWidth:(CGFloat)borderWidth + linespace:(CGFloat)linespace + preeditLinespace:(CGFloat)preeditLinespace + alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging linear:(BOOL)linear @@ -256,7 +256,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRect contentRect; @property(nonatomic, readonly) NSMutableArray *candidatePathRefs; @property(nonatomic, readonly) NSMutableArray *pagingRects; -@property(nonatomic, readonly) NSUInteger buttonEffect; +@property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, readonly) CAShapeLayer *shape; @@ -268,7 +268,7 @@ - (void) drawViewWith:(NSArray *)candidateRanges preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange - buttonEffect:(NSUInteger)buttonEffect; + pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -345,13 +345,13 @@ - (void) drawViewWith:(NSArray*)candidateRanges preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange - buttonEffect:(NSUInteger)buttonEffect { + pagingButton:(NSUInteger)pagingButton { _candidateRanges = candidateRanges; _highlightedIndex = highlightedIndex; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; - _buttonEffect = buttonEffect; + _pagingButton = pagingButton; self.needsDisplay = YES; } @@ -729,16 +729,16 @@ - (void)drawRect:(NSRect)dirtyRect { } } if (theme.highlightedPreeditColor) { - if (_buttonEffect == NSPageUpFunctionKey) { + if (_pagingButton == NSPageUpFunctionKey) { [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; [pageUpPath fill]; - } if (_buttonEffect == NSBeginFunctionKey) { + } if (_pagingButton == NSBeginFunctionKey) { [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; [pageUpPath fill]; - } else if (_buttonEffect == NSPageDownFunctionKey) { + } else if (_pagingButton == NSPageDownFunctionKey) { [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; [pageDownPath fill]; - } else if (_buttonEffect == NSEndFunctionKey) { + } else if (_pagingButton == NSEndFunctionKey) { [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; [pageDownPath fill]; } @@ -755,9 +755,9 @@ - (void)drawRect:(NSRect)dirtyRect { [borderPath stroke]; } - NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; - [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; - +// NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; +// [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; + [_textView setTextContainerInset:theme.edgeInset]; } - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { @@ -800,7 +800,7 @@ @implementation SquirrelPanel { NSArray *_labels; NSUInteger _index; NSUInteger _pageNum; - NSInteger _buttonEffect; + NSInteger _turnPage; BOOL _lastPage; NSUInteger _cursorIndex; @@ -831,7 +831,7 @@ - (BOOL)showPaging { void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); - long i = 0; + NSUInteger i = 0; while (i < text.length) { NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { @@ -891,7 +891,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; - pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize/1.5]; + pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize]; NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; @@ -923,6 +923,7 @@ - (instancetype)init { self.level = CGShieldingWindowLevel(); self.hasShadow = YES; self.opaque = NO; + self.floatingPanel = YES; self.backgroundColor = [NSColor clearColor]; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.frame]; @@ -936,6 +937,7 @@ - (instancetype)init { [contentView addSubview:_back]; } [contentView addSubview:_view]; + [contentView addSubview:_view.textView]; self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; @@ -955,7 +957,8 @@ - (NSPoint)mousePosition { - (void)sendEvent:(NSEvent *)event { switch (event.type) { - case NSEventTypeLeftMouseDown: { + case NSEventTypeLeftMouseDown: + case NSEventTypeRightMouseDown: { NSPoint point = [self mousePosition]; NSInteger index = NSNotFound; if ([_view clickAtPoint:point index:&index]) { @@ -968,21 +971,31 @@ - (void)sendEvent:(NSEvent *)event { case NSEventTypeLeftMouseUp: { NSPoint point = [self mousePosition]; NSInteger index = NSNotFound; - if ([_view clickAtPoint: point index:&index]) { + if ([_view clickAtPoint:point index:&index]) { if (((index >= 0 && index < _candidates.count) || index == NSPageUpFunctionKey || index == NSPageDownFunctionKey) && index == _index) { - [_inputController selectCandidate:index]; + [_inputController actionWithCandidate:index]; + } + } + } break; + case NSEventTypeRightMouseUp: { + NSPoint point = [self mousePosition]; + NSInteger index = NSNotFound; + if ([_view clickAtPoint:point index:&index]) { + if ((index >= 0 && index < _candidates.count) && index == _index) { + [_inputController actionWithCandidate:-1-index]; // negative index for deletion } } } break; case NSEventTypeMouseEntered: { + [[NSCursor arrowCursor] push]; self.acceptsMouseMovedEvents = YES; } break; case NSEventTypeMouseExited: { self.acceptsMouseMovedEvents = NO; if (_cursorIndex != _index) { [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:_index pageNum:_pageNum lastPage:_lastPage buttonEffect:NSNotFound update:NO]; + highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } } break; case NSEventTypeMouseMoved: { @@ -990,12 +1003,14 @@ - (void)sendEvent:(NSEvent *)event { NSInteger index = NSNotFound; if ([_view clickAtPoint: point index:&index]) { if (index >= 0 && index < _candidates.count && _cursorIndex != index) { + [[NSCursor pointingHandCursor] push]; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:index pageNum:_pageNum lastPage:_lastPage buttonEffect:NSNotFound update:NO]; + highlighted:index pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if (index == NSPageUpFunctionKey || index == NSPageDownFunctionKey || - index == NSBeginFunctionKey || index == NSEndFunctionKey) { + index == NSBeginFunctionKey || index == NSEndFunctionKey) { // borrow corresponding unicodes for readability + [[NSCursor pointingHandCursor] push]; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:_index pageNum:_pageNum lastPage:_lastPage buttonEffect:index update:NO]; + highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:index update:NO]; } } } break; @@ -1041,10 +1056,11 @@ - (void)show { _maxTextWidth = theme.vertical ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; + CGFloat maxTextHeight = theme.vertical ? NSWidth(_screenRect) - theme.edgeInset.width * 2 : NSHeight(_screenRect) - theme.edgeInset.height * 2; if (textWidth > _maxTextWidth) { textWidth = _maxTextWidth; } - _view.textView.textContainer.containerSize = NSMakeSize(textWidth, 0); + _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); NSRect contentRect = _view.contentRect; if (contentRect.size.width > textWidth) { contentRect.size.width = textWidth; @@ -1057,7 +1073,7 @@ - (void)show { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, 0); + _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); } } @@ -1077,7 +1093,7 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_view.preeditRange].size; - windowRect.origin.x += preeditSize.height + theme.edgeInset.width / 2; + windowRect.origin.x += preeditSize.height + theme.edgeInset.width; } } else { windowRect.size = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, @@ -1091,7 +1107,7 @@ - (void)show { } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width / 2, + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } @@ -1111,14 +1127,24 @@ - (void)show { [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { +// self.contentView.boundsRotation = -90; +// [self.contentView setBoundsSize:NSMakeSize(insetRectSize.height, insetRectSize.width)]; +// [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; self.contentView.boundsRotation = -90.0; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; +// [self.contentView setBoundsSize:NSMakeSize(windowRect.size.height, windowRect.size.width)]; + [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; } else { self.contentView.boundsRotation = 0; + _view.textView.boundsRotation = 0; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; +// [self.contentView setBoundsSize:NSMakeSize(windowRect.size.width, windowRect.size.height)]; + [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; } - BOOL translucency = theme.translucency; [_view setFrame:self.contentView.bounds]; + [_view.textView setFrame:self.contentView.bounds]; + BOOL translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; @@ -1153,7 +1179,7 @@ - (void)showPreedit:(NSString *)preedit highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage - buttonEffect:(NSUInteger)buttonEffect + turnPage:(NSUInteger)turnPage update:(BOOL)update { if (update) { _preedit = preedit; @@ -1172,12 +1198,12 @@ - (void)showPreedit:(NSString *)preedit } _cursorIndex = index; - if (buttonEffect == NSPageUpFunctionKey || buttonEffect == NSBeginFunctionKey) { - _buttonEffect = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } else if (buttonEffect == NSPageDownFunctionKey || buttonEffect == NSEndFunctionKey) { - _buttonEffect = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + if (turnPage == NSPageUpFunctionKey || turnPage == NSBeginFunctionKey) { + _turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; + } else if (turnPage == NSPageDownFunctionKey || turnPage == NSEndFunctionKey) { + _turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; } else { - _buttonEffect = NSNotFound; + _turnPage = NSNotFound; } if (numCandidates || (preedit && preedit.length)) { @@ -1301,7 +1327,7 @@ - (void)showPreedit:(NSString *)preedit suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString - attributes:labelAttrs]]; + attributes:attrs]]; } fixDefaultFont(line); @@ -1320,7 +1346,7 @@ - (void)showPreedit:(NSString *)preedit } else { paragraphStyleCandidate.headIndent = labelWidth; } - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:separtatorString attributes:theme.labelAttrs]; + NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; if (i > 0) { [text appendAttributedString:separator]; @@ -1359,13 +1385,16 @@ - (void)showPreedit:(NSString *)preedit [text appendAttributedString:paging]; } + // extra line fragment will not actually be drawn but ensures the spacing after the last line/paragraph + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.showPaging ? theme.pagingAttrs : theme.attrs]]; + [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; // text done! - [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange buttonEffect:_buttonEffect]; - + [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:_turnPage]; + [self show]; } @@ -1398,7 +1427,7 @@ - (void)showStatus:(NSString *)message { _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:@[] highlightedIndex:NSNotFound preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange buttonEffect:NSNotFound]; + [_view drawViewWith:@[] highlightedIndex:NSNotFound preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange pagingButton:NSNotFound]; [self show]; if (_statusTimer) { @@ -1438,8 +1467,7 @@ - (void)hideStatus:(NSTimer *)timer { // If the font name is not valid, NSFontDescriptor will still create something for us. // However, when we draw the actual text, Squirrel will crash if there is any font descriptor // with invalid font name. - [validFontDescriptors addObject:[NSFontDescriptor fontDescriptorWithName:fontName - size:0.0]]; + [validFontDescriptors addObject:[NSFontDescriptor fontDescriptorWithName:fontName size:0.0]]; } } if (validFontDescriptors.count == 0) { @@ -1748,7 +1776,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; } } - NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize/1.5]; + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize]; CGFloat lineHeight = font.ascender - font.descender; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; @@ -1762,7 +1790,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle.paragraphSpacing = spacing/2; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentRight; + pagingParagraphStyle.alignment = NSTextAlignmentLeft; + pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols @@ -1798,7 +1827,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSSuperscriptAttributeName] = @(-1); +// pagingAttrs[NSSuperscriptAttributeName] = @(-1); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; From cc355be901566853955ba204fbec9cfc110dd376 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 02:15:00 +0200 Subject: [PATCH 059/164] scale for small font size --- SquirrelPanel.m | 336 +++++++++++++++++++++++++----------------------- 1 file changed, 176 insertions(+), 160 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a9e024fb4..a1ae10eb3 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -72,6 +72,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; +@property(nonatomic, readonly) CGFloat scaleFactor; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; @property(nonatomic, readonly) BOOL showPaging; @@ -113,6 +114,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace + scaleFactor:(CGFloat)scaleFactor alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging @@ -193,6 +195,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace + scaleFactor:(CGFloat)scaleFactor alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging @@ -205,6 +208,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; + _scaleFactor = scaleFactor; _alpha = alpha; _translucency = translucency; _showPaging = showPaging; @@ -254,7 +258,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; -@property(nonatomic, readonly) NSMutableArray *candidatePathRefs; +@property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingRects; @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; @@ -316,9 +320,10 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _textView.layoutManager.usesFontLeading = NO; _textView.layoutManager.typesetterBehavior = NSTypesetterBehavior_10_4; _defaultTheme = [[SquirrelTheme alloc] init]; - _candidatePathRefs = [NSMutableArray arrayWithCapacity:_candidateRanges.count]; + _candidatePaths = [NSMutableArray arrayWithCapacity:_candidateRanges.count]; _pagingRects = [NSMutableArray arrayWithCapacity:2]; _shape = [[CAShapeLayer alloc] init]; + _shape.allowsEdgeAntialiasing = YES; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } @@ -450,13 +455,13 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; + NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { *bodyRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, endPoint.x-startPoint.x, NSHeight(leadingLineRect)); } else { *leadingRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, NSWidth(leadingLineRect)-startPoint.x, NSHeight(leadingLineRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; + NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange]; *trailingRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMinY(trailingLineRect)+edgeInset.height, endPoint.x, NSHeight(trailingLineRect)); NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); if (bodyLineRange.length > 0) { @@ -497,8 +502,8 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe // If the point is outside the innerBox, will extend to reach the outerBox void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerBorder) { - for (NSUInteger i = 0; i < vertex.count; i += 1){ - NSPoint point = (vertex[i]).pointValue; + for (NSUInteger i = 0; i < vertex.count; i += 1) { + NSPoint point = [vertex[i] pointValue]; if (point.x < innerBorder.origin.x) { point.x = outerBorder.origin.x; } else if (point.x > innerBorder.origin.x+innerBorder.size.width) { @@ -510,18 +515,29 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB point.y = outerBorder.origin.y+outerBorder.size.height; } [vertex replaceObjectAtIndex:i withObject:@(point)]; - vertex[i] = @(point); +// vertex[i] = @(point); } } -CGMutablePathRef polygonPathForVertex(NSArray *vertex) { - CGMutablePathRef path = CGPathCreateMutable(); - CGPathMoveToPoint(path, NULL, vertex[vertex.count-1].pointValue.x, vertex[vertex.count-1].pointValue.y); - for (NSUInteger i = 0; i < vertex.count; i += 1){ - CGPathAddLineToPoint(path, NULL, vertex[i].pointValue.x, vertex[i].pointValue.y); +// If the point is outside the boundBox, will shrink to the border of boundBox +void shrink(NSMutableArray *vertex, NSRect boundBox) { + for (NSUInteger i = 0; i < vertex.count; i += 1) { + NSPoint point = [vertex[i] pointValue]; + if (point.x < NSMinX(boundBox)) { + point.x = NSMinX(boundBox); + } + if (point.y < NSMinY(boundBox)) { + point.y = NSMinY(boundBox); + } + if (point.x > NSMaxX(boundBox)) { + point.x = NSMaxX(boundBox); + } + if (point.y > NSMaxY(boundBox)) { + point.y = NSMaxY(boundBox); + } + [vertex replaceObjectAtIndex:i withObject:@(point)]; +// vertex[i] = @(point); } - CGPathCloseSubpath(path); - return path; } // All draws happen here @@ -529,11 +545,9 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *backgroundPath; NSBezierPath *borderPath; NSBezierPath *highlightedPath; - NSBezierPath *highlightedPath2; NSBezierPath *highlightedPreeditPath; - NSBezierPath *highlightedPreeditPath2; - NSBezierPath *nonCandidatesPath; - NSBezierPath *candidatesPath; + NSBezierPath *nonCandidateBlockPath; + NSBezierPath *candidateBlockPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; SquirrelTheme * theme = self.currentTheme; @@ -557,12 +571,13 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw paging Rect NSRect pagingRect = NSZeroRect; if (_pagingRange.length > 0) { - pagingRect = [self contentRectForRange:_pagingRange]; - pagingRect.size.width = backgroundRect.size.width; - pagingRect.size.height += theme.edgeInset.height; - pagingRect.origin.x = backgroundRect.origin.x; - pagingRect.origin.y += theme.edgeInset.height; - + if (!theme.linear) { + pagingRect = [self contentRectForRange:_pagingRange]; + pagingRect.size.width = backgroundRect.size.width; + pagingRect.size.height += theme.edgeInset.height; + pagingRect.origin.x = backgroundRect.origin.x; + pagingRect.origin.y += theme.edgeInset.height; + } NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 2)]; pageUpRect.origin.y += theme.edgeInset.height; pageUpRect.origin.x += theme.edgeInset.width; @@ -572,8 +587,8 @@ - (void)drawRect:(NSRect)dirtyRect { pageDownRect.origin.x += theme.edgeInset.width; _pagingRects[1] = [NSValue valueWithRect:pageDownRect]; if (theme.highlightedPreeditColor != nil) { - pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*pageUpRect.size.height, 0.28*pageUpRect.size.height); - pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*pageDownRect.size.height, 0.28*pageDownRect.size.height); + pageUpPath = [NSBezierPath bezierPathWithRoundedRect:pageUpRect xRadius:pageUpRect.size.height * 0.2 yRadius:pageUpRect.size.width * 0.2]; + pageDownPath = [NSBezierPath bezierPathWithRoundedRect:pageDownRect xRadius:pageDownRect.size.height * 0.2 yRadius:pageDownRect.size.width * 0.2]; } } @@ -586,12 +601,12 @@ - (void)drawRect:(NSRect)dirtyRect { outerBox.size.height -= theme.edgeInset.height; outerBox.origin.y += theme.edgeInset.height; } - if (_pagingRange.length == 0) { + if (_pagingRange.length == 0 || theme.linear) { outerBox.size.height -= theme.edgeInset.height; } NSRect innerBox = NSInsetRect(outerBox, 1, theme.linespace/2+1); if (theme.preeditBackgroundColor != nil) { - candidatesPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + candidateBlockPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } if (theme.linear) { @@ -612,18 +627,11 @@ - (void)drawRect:(NSRect)dirtyRect { candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) copy]; } - CGMutablePathRef candidatePathRef = polygonPathForVertex(candidatePoints); + NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (candidatePoints2.count > 0) { - CGPathAddPath(candidatePathRef, NULL, polygonPathForVertex(candidatePoints2)); - } - _candidatePathRefs[i] = (__bridge NSValue * _Nonnull)(candidatePathRef); - - if (i == _highlightedIndex && theme.highlightedStripColor != nil) { - highlightedPath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - if (candidatePoints2.count > 0) { - highlightedPath2 = drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - } + [candidatePath appendBezierPath:drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } + _candidatePaths[i] = candidatePath; } } else { for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { @@ -635,18 +643,19 @@ - (void)drawRect:(NSRect)dirtyRect { NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; expand(candidatePoints, innerBox, outerBox); - CGMutablePathRef candidatePathRef = polygonPathForVertex(candidatePoints); - _candidatePathRefs[i] = (__bridge NSValue * _Nonnull)(candidatePathRef); - - if (i == _highlightedIndex && theme.highlightedStripColor != nil) { - highlightedPath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - } + NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + _candidatePaths[i] = candidatePath; } } + if (theme.highlightedStripColor != nil) { + highlightedPath = _candidatePaths[_highlightedIndex]; + } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { + NSRect boundBox = preeditRect; + boundBox.size.height -= theme.preeditLinespace/2; NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; @@ -661,9 +670,11 @@ - (void)drawRect:(NSRect)dirtyRect { } else { highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } + shrink(highlightedPreeditPoints, boundBox); highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - highlightedPreeditPath2 = drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + shrink(highlightedPreeditPoints2, boundBox); + [highlightedPreeditPath appendBezierPath:drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } } @@ -675,7 +686,6 @@ - (void)drawRect:(NSRect)dirtyRect { borderPath.lineWidth = theme.borderWidth; // Nothing should extend beyond backgroundPath [backgroundPath addClip]; - // This block of code enables independent transparencies in highlighted colour and background colour. // Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. #if 0 @@ -713,20 +723,17 @@ - (void)drawRect:(NSRect)dirtyRect { [backgroundPath fill]; if (theme.preeditBackgroundColor && (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { - nonCandidatesPath = [backgroundPath copy]; - if (![candidatesPath isEmpty]) { - [nonCandidatesPath appendBezierPath:candidatesPath]; + nonCandidateBlockPath = [backgroundPath copy]; + if (![candidateBlockPath isEmpty]) { + [nonCandidateBlockPath appendBezierPath:candidateBlockPath]; + [nonCandidateBlockPath setWindingRule:NSEvenOddWindingRule]; } - [nonCandidatesPath setWindingRule:NSEvenOddWindingRule]; [theme.preeditBackgroundColor setFill]; - [nonCandidatesPath fill]; + [nonCandidateBlockPath fill]; } if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { [theme.highlightedStripColor setFill]; [highlightedPath fill]; - if (![highlightedPath2 isEmpty]) { - [highlightedPath2 fill]; - } } if (theme.highlightedPreeditColor) { if (_pagingButton == NSPageUpFunctionKey) { @@ -745,9 +752,6 @@ - (void)drawRect:(NSRect)dirtyRect { if(![highlightedPreeditPath isEmpty]) { [theme.highlightedPreeditColor setFill]; [highlightedPreeditPath fill]; - if (![highlightedPreeditPath2 isEmpty]) { - [highlightedPreeditPath2 fill]; - } } } if (theme.borderColor && (theme.borderWidth > 0)) { @@ -755,25 +759,21 @@ - (void)drawRect:(NSRect)dirtyRect { [borderPath stroke]; } -// NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; -// [_textView.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textField.origin]; [_textView setTextContainerInset:theme.edgeInset]; } - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { - if (CGPathContainsPoint(_shape.path, nil, _point, NO)) { - NSPoint point = NSMakePoint(_point.x - self.textView.textContainerInset.width, - _point.y - self.textView.textContainerInset.height); - if (_pagingRects[0] != nil && NSPointInRect(point, _pagingRects[0].rectValue)) { + if (CGPathContainsPoint(_shape.path, NULL, _point, NO)) { + if (_pagingRects[0] != nil && NSPointInRect(_point, _pagingRects[0].rectValue)) { *_index = NSPageUpFunctionKey; return YES; - } else if (_pagingRects[1] != nil && NSPointInRect(point, _pagingRects[1].rectValue)) { + } else if (_pagingRects[1] != nil && NSPointInRect(_point, _pagingRects[1].rectValue)) { *_index = NSPageDownFunctionKey; return YES; } - for (NSUInteger i = 0; i < _candidatePathRefs.count; i += 1) { - CGPathRef pathRef = (__bridge CGPathRef)([_candidatePathRefs objectAtIndex:i]); - if (CGPathContainsPoint(pathRef, NULL, point, NO)) { + for (NSUInteger i = 0; i < _candidatePaths.count; i += 1) { + CGPathRef candidatePath = [_candidatePaths objectAtIndex:i].quartzPath; + if (CGPathContainsPoint(candidatePath, NULL, _point, NO)) { *_index = i; return YES; } @@ -891,7 +891,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; - pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize]; + pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize/1.5]; NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; @@ -923,7 +923,6 @@ - (instancetype)init { self.level = CGShieldingWindowLevel(); self.hasShadow = YES; self.opaque = NO; - self.floatingPanel = YES; self.backgroundColor = [NSColor clearColor]; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.frame]; @@ -988,7 +987,6 @@ - (void)sendEvent:(NSEvent *)event { } } break; case NSEventTypeMouseEntered: { - [[NSCursor arrowCursor] push]; self.acceptsMouseMovedEvents = YES; } break; case NSEventTypeMouseExited: { @@ -1003,12 +1001,10 @@ - (void)sendEvent:(NSEvent *)event { NSInteger index = NSNotFound; if ([_view clickAtPoint: point index:&index]) { if (index >= 0 && index < _candidates.count && _cursorIndex != index) { - [[NSCursor pointingHandCursor] push]; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:index pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if (index == NSPageUpFunctionKey || index == NSPageDownFunctionKey || index == NSBeginFunctionKey || index == NSEndFunctionKey) { // borrow corresponding unicodes for readability - [[NSCursor pointingHandCursor] push]; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:index update:NO]; } @@ -1035,7 +1031,7 @@ - (void)getCurrentScreen { } } -// Get the window size, the windows will be the dirtyRect in SquirrelView.drawRect +// Get the window size, its bounds (insetRect) will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; SquirrelTheme *theme = _view.currentTheme; @@ -1048,15 +1044,16 @@ - (void)show { } //Break line if the text is too long, based on screen size. - CGFloat textWidth = _view.textView.textStorage.size.width; NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12); + CGFloat fontScale = currentFont.pointSize / 12.0 / theme.scaleFactor; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12.0); _maxTextWidth = theme.vertical - ? NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2; - CGFloat maxTextHeight = theme.vertical ? NSWidth(_screenRect) - theme.edgeInset.width * 2 : NSHeight(_screenRect) - theme.edgeInset.height * 2; + ? NSHeight(_screenRect) * textWidthRatio * theme.scaleFactor - theme.edgeInset.height * 2 + : NSWidth(_screenRect) * textWidthRatio * theme.scaleFactor - theme.edgeInset.width * 2; + CGFloat maxTextHeight = theme.vertical + ? NSWidth(_screenRect) * theme.scaleFactor - theme.edgeInset.width * 2 + : NSHeight(_screenRect) * theme.scaleFactor - theme.edgeInset.height * 2; if (textWidth > _maxTextWidth) { textWidth = _maxTextWidth; } @@ -1068,7 +1065,7 @@ - (void)show { // in vertical mode, the width and height are interchanged if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || - (!theme.vertical && NSMinX(_position)+MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2 > NSMaxX(_screenRect))) { + (!theme.vertical && NSMinX(_position)+(MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2)/theme.scaleFactor > NSMaxX(_screenRect))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { @@ -1080,68 +1077,66 @@ - (void)show { // the sweep direction of the client app changes the behavior of adjusting squirrel panel position NSRect windowRect; bool sweepVertical = NSWidth(_position) > NSHeight(_position); + NSSize insetRectSize = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, + contentRect.size.height + theme.edgeInset.height * 2); if (theme.vertical) { - windowRect.size = NSMakeSize(contentRect.size.height + theme.edgeInset.height * 2, - contentRect.size.width + theme.edgeInset.width * 2); + windowRect.size = NSMakeSize(insetRectSize.height / theme.scaleFactor, insetRectSize.width / theme.scaleFactor); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : kOffsetHeight) - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight) + theme.edgeInset.width; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight) + theme.edgeInset.width/theme.scaleFactor; } // Make the first candidate fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_view.preeditRange].size; - windowRect.origin.x += preeditSize.height + theme.edgeInset.width; + windowRect.origin.x += (preeditSize.height + theme.edgeInset.width) / theme.scaleFactor; } } else { - windowRect.size = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, - contentRect.size.height + theme.edgeInset.height * 2); + windowRect.size = NSMakeSize(insetRectSize.width / theme.scaleFactor, insetRectSize.height / theme.scaleFactor); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width; + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width / theme.scaleFactor; } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width, + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width / theme.scaleFactor, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { - windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (sweepVertical ? NSMinX(_position)-kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(_screenRect)) { - windowRect.origin.x = sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(_screenRect); + windowRect.origin.x = sweepVertical ? NSMaxX(_position)+kOffsetHeight : NSMinX(_screenRect); } if (NSMinY(windowRect) < NSMinY(_screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position) + kOffsetHeight); + windowRect.origin.y = sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position)+kOffsetHeight; } if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); } [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { -// self.contentView.boundsRotation = -90; -// [self.contentView setBoundsSize:NSMakeSize(insetRectSize.height, insetRectSize.width)]; -// [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; self.contentView.boundsRotation = -90.0; _view.textView.boundsRotation = 0; + [self.contentView setBoundsSize:NSMakeSize(insetRectSize.height, insetRectSize.width)]; [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; -// [self.contentView setBoundsSize:NSMakeSize(windowRect.size.height, windowRect.size.width)]; [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; } else { self.contentView.boundsRotation = 0; _view.textView.boundsRotation = 0; + [self.contentView setBoundsSize:NSMakeSize(insetRectSize.width, insetRectSize.height)]; [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; -// [self.contentView setBoundsSize:NSMakeSize(windowRect.size.width, windowRect.size.height)]; [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; } + [_view setFrame:self.contentView.bounds]; [_view.textView setFrame:self.contentView.bounds]; BOOL translucency = theme.translucency; @@ -1261,12 +1256,27 @@ - (void)showPreedit:(NSString *)preedit } } + // prepare paging and separator for width calculation but no insertion yet + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" + attributes:theme.pagingAttrs]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀\ufe0e" : @"◁\ufe0e").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:theme.linear ? @" %lu " : @"\t%lu\t", pageNum+1] attributes:theme.pagingAttrs]]; +// [paging appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? @" " : @"\t" +// attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷\ufe0e" : @"▶\ufe0e").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs]]; + fixDefaultFont(paging); + CGFloat pagingWidth = theme.showPaging ? NSMaxX([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + + CGFloat separatorWidth = theme.linear ? NSMaxX([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + CGFloat lineWidth = 0.0 - separatorWidth; + BOOL multiLine = NO; + // candidates + NSUInteger candidateBlockStart = text.length; NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; - CGFloat separatorWidth = NSWidth([[[NSAttributedString alloc] initWithString:@" " attributes:theme.labelAttrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); - CGFloat candidateTextWidth = 0 - separatorWidth; - NSUInteger i; - for (i = 0; i < candidates.count; ++i) { + for (NSUInteger i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; @@ -1334,16 +1344,21 @@ - (void)showPreedit:(NSString *)preedit NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; // determine if the line is too wide and line break is needed, based on screen size. NSString *separtatorString = @"\u2029"; + CGFloat candidateWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); if (theme.linear) { - CGFloat lineWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); - candidateTextWidth += separatorWidth + lineWidth; - if (candidateTextWidth > _maxTextWidth) { + if (i == numCandidates-1) { + candidateWidth += separatorWidth + pagingWidth; + } + if (lineWidth + separatorWidth + candidateWidth > _maxTextWidth) { separtatorString = @"\u2028"; - candidateTextWidth = lineWidth; + multiLine = YES; + lineWidth = candidateWidth; } else { separtatorString = @" "; + lineWidth += separatorWidth + candidateWidth; } } else { + lineWidth = MAX(lineWidth, MIN(candidateWidth, _maxTextWidth)); paragraphStyleCandidate.headIndent = labelWidth; } NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; @@ -1357,37 +1372,27 @@ - (void)showPreedit:(NSString *)preedit [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length, line.length)]]; [text appendAttributedString:line]; } - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.labelAttrs]]; // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates && theme.showPaging) { - - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" - attributes:theme.pagingAttrs]; - - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀" : @"◁") - attributes:theme.pagingAttrs]]; - - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] - attributes:theme.pagingAttrs]]; - - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷" : @"▶") - attributes:theme.pagingAttrs]]; - - fixDefaultFont(paging); - [paging addAttribute:NSParagraphStyleAttributeName - value:theme.pagingParagraphStyle - range:NSMakeRange(0, paging.length)]; - + [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (multiLine ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; pagingRange = NSMakeRange(text.length, paging.length); - [text appendAttributedString:paging]; + if (theme.linear) { + [text appendAttributedString:paging]; + NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + paragraphStyleCandidate.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:floor(_maxTextWidth)]]; + [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; + } else { + NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:floor(lineWidth/2)], + [[NSTextTab alloc] initWithType:NSRightTabStopType location:floor(lineWidth)]]; + [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; + [text appendAttributedString:paging]; + } } - // extra line fragment will not actually be drawn but ensures the spacing after the last line/paragraph - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.showPaging ? theme.pagingAttrs : theme.attrs]]; - [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1735,28 +1740,29 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSize == 0) { commentFontSize = fontSize; } + CGFloat scaleFactor = MAX(1.0, kDefaultFontSize/fontSize); NSFontDescriptor *fontDescriptor = nil; NSFont *font = nil; if (fontName != nil) { fontDescriptor = getFontDescriptor(fontName); if (fontDescriptor != nil) { - font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize]; + font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize * scaleFactor]; } } if (font == nil) { // use default font - font = [NSFont userFontOfSize:fontSize]; + font = [NSFont userFontOfSize:fontSize * scaleFactor]; } NSFontDescriptor *labelFontDescriptor = nil; NSFont *labelFont = nil; if (labelFontName != nil) { labelFontDescriptor = getFontDescriptor(labelFontName); if (labelFontDescriptor != nil) { - labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; + labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize * scaleFactor]; } } if (labelFont == nil) { - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize * scaleFactor weight:NSFontWeightRegular]; } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1766,31 +1772,31 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFontDescriptor = fontDescriptor; } if (commentFontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:commentFontDescriptor size:commentFontSize]; + commentFont = [NSFont fontWithDescriptor:commentFontDescriptor size:commentFontSize * scaleFactor]; } } if (commentFont == nil) { if (fontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:fontDescriptor size:commentFontSize]; + commentFont = [NSFont fontWithDescriptor:fontDescriptor size:commentFontSize * scaleFactor]; } else { - commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; + commentFont = [NSFont fontWithName:font.fontName size:commentFontSize * scaleFactor]; } } - NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize]; + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize/1.5 * scaleFactor]; CGFloat lineHeight = font.ascender - font.descender; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; paragraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2; - paragraphStyle.lineSpacing = lineSpacing/2; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2 * scaleFactor; + paragraphStyle.lineSpacing = lineSpacing/2 * scaleFactor; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; preeditParagraphStyle.minimumLineHeight = lineHeight; - preeditParagraphStyle.paragraphSpacing = spacing/2; + preeditParagraphStyle.paragraphSpacing = spacing/2 * scaleFactor; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentLeft; + pagingParagraphStyle.alignment = NSTextAlignmentRight; pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left @@ -1818,16 +1824,16 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; pagingAttrs[NSFontAttributeName] = pagingFont; - attrs[NSBaselineOffsetAttributeName] = @(baseOffset); - highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); -// pagingAttrs[NSSuperscriptAttributeName] = @(-1); + attrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); +// pagingAttrs[NSSuperscriptAttributeName] = @(1); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -1858,15 +1864,24 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo pagingAttrs[NSForegroundColorAttributeName] = candidateLabelColor; attrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - labelAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - highlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + labelAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + highlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - commentAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; preeditAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + attrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + labelAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + highlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + labelHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + commentAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + commentHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + preeditAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + preeditHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + [theme setStatusMessageType:statusMessageType]; [theme setAttrs:attrs @@ -1890,16 +1905,17 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)); - borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)); + borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; + borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:cornerRadius - hilitedCornerRadius:hilitedCornerRadius + [theme setCornerRadius:cornerRadius * scaleFactor + hilitedCornerRadius:hilitedCornerRadius * scaleFactor edgeInset:edgeInset borderWidth:MAX(borderHeight, borderWidth) - linespace:lineSpacing - preeditLinespace:spacing + linespace:lineSpacing * scaleFactor + preeditLinespace:spacing * scaleFactor + scaleFactor:scaleFactor alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency showPaging:showPaging From cb31db953b00bf86861e099cb1a7b5d3fd81dc14 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 18:15:07 +0200 Subject: [PATCH 060/164] background image --- SquirrelPanel.m | 4 ++++ librime | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a1ae10eb3..3ee21eb23 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -719,6 +719,10 @@ - (void)drawRect:(NSRect)dirtyRect { [preeditPath setWindingRule:NSEvenOddWindingRule]; #endif + if (theme.backgroundImage) { + [theme.backgroundImage setFill]; + [backgroundPath fill]; + } [theme.backgroundColor setFill]; [backgroundPath fill]; if (theme.preeditBackgroundColor && diff --git a/librime b/librime index 2dc90787f..3743c0a59 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 2dc90787f5d9d5e90d91d99ab2212b85f34537da +Subproject commit 3743c0a592308ca7cff15b2b565e274809f671c7 From 616fc70d037760fbac207a7a6e058b74b74340a2 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 18:56:37 +0200 Subject: [PATCH 061/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 3743c0a59..8abab3397 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 3743c0a592308ca7cff15b2b565e274809f671c7 +Subproject commit 8abab3397caaf1a2bea58f46815caee6f3d39667 From bd3cfe71e6f0d56551bb221038033eabfa0acc24 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 23:33:03 +0200 Subject: [PATCH 062/164] Update SquirrelPanel.m --- SquirrelPanel.m | 120 ++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 64 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 3ee21eb23..7c8b855ad 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -6,41 +6,38 @@ @implementation NSBezierPath (BezierPathQuartzUtilities) // This method works only in OS X v10.2 and later. - (CGPathRef)quartzPath { - NSInteger i, numElements; // Need to begin a path here. CGPathRef immutablePath = NULL; - // Then draw the path elements. - numElements = [self elementCount]; + NSUInteger numElements = [self elementCount]; if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; BOOL didClosePath = YES; - for (i = 0; i < numElements; i++) { + for (NSUInteger i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { - case NSMoveToBezierPathElement: - CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); - break; - case NSLineToBezierPathElement: - CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); - didClosePath = NO; - break; - case NSCurveToBezierPathElement: - CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, - points[1].x, points[1].y, - points[2].x, points[2].y); - didClosePath = NO; - break; - case NSClosePathBezierPathElement: - CGPathCloseSubpath(path); - didClosePath = YES; - break; + case NSMoveToBezierPathElement: + CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); + break; + case NSLineToBezierPathElement: + CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); + didClosePath = NO; + break; + case NSCurveToBezierPathElement: + CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, + points[1].x, points[1].y, + points[2].x, points[2].y); + didClosePath = NO; + break; + case NSClosePathBezierPathElement: + CGPathCloseSubpath(path); + didClosePath = YES; + break; } } - - // Be sure the path is closed or Quartz may not do valid hit detection. + // Be sure the path is closed or Quartz may not do valid hit detection. if (!didClosePath) { - CGPathCloseSubpath(path); + CGPathCloseSubpath(path); } immutablePath = CGPathCreateCopy(path); CGPathRelease(path); @@ -323,7 +320,6 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _candidatePaths = [NSMutableArray arrayWithCapacity:_candidateRanges.count]; _pagingRects = [NSMutableArray arrayWithCapacity:2]; _shape = [[CAShapeLayer alloc] init]; - _shape.allowsEdgeAntialiasing = YES; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } @@ -446,7 +442,6 @@ BOOL nearEmptyRect(NSRect rect) { // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { NSLayoutManager *layoutManager = _textView.layoutManager; - NSTextContainer *textContainer = _textView.textContainer; NSSize edgeInset = self.currentTheme.edgeInset; *leadingRect = NSZeroRect; *bodyRect = NSZeroRect; @@ -515,7 +510,6 @@ void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerB point.y = outerBorder.origin.y+outerBorder.size.height; } [vertex replaceObjectAtIndex:i withObject:@(point)]; -// vertex[i] = @(point); } } @@ -536,7 +530,6 @@ void shrink(NSMutableArray *vertex, NSRect boundBox) { point.y = NSMaxY(boundBox); } [vertex replaceObjectAtIndex:i withObject:@(point)]; -// vertex[i] = @(point); } } @@ -655,7 +648,9 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { NSRect boundBox = preeditRect; - boundBox.size.height -= theme.preeditLinespace/2; + if (_highlightedIndex != NSNotFound) { + boundBox.size.height -= theme.preeditLinespace/2; + } NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; @@ -679,7 +674,7 @@ - (void)drawRect:(NSRect)dirtyRect { } backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); - _shape.path = backgroundPath.quartzPath; + _shape.path = [backgroundPath quartzPath]; NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); @@ -729,8 +724,8 @@ - (void)drawRect:(NSRect)dirtyRect { (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { nonCandidateBlockPath = [backgroundPath copy]; if (![candidateBlockPath isEmpty]) { - [nonCandidateBlockPath appendBezierPath:candidateBlockPath]; - [nonCandidateBlockPath setWindingRule:NSEvenOddWindingRule]; + [nonCandidateBlockPath appendBezierPath:[candidateBlockPath bezierPathByReversingPath]]; + [nonCandidateBlockPath setWindingRule:NSNonZeroWindingRule]; } [theme.preeditBackgroundColor setFill]; [nonCandidateBlockPath fill]; @@ -775,9 +770,8 @@ - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { *_index = NSPageDownFunctionKey; return YES; } - for (NSUInteger i = 0; i < _candidatePaths.count; i += 1) { - CGPathRef candidatePath = [_candidatePaths objectAtIndex:i].quartzPath; - if (CGPathContainsPoint(candidatePath, NULL, _point, NO)) { + for (NSUInteger i = 0; i < _candidatePaths.count; i++) { + if ([_candidatePaths[i] containsPoint:_point]) { *_index = i; return YES; } @@ -1024,9 +1018,7 @@ - (void)getCurrentScreen { // get current screen _screenRect = [NSScreen mainScreen].frame; NSArray *screens = [NSScreen screens]; - - NSUInteger i; - for (i = 0; i < screens.count; ++i) { + for (NSUInteger i = 0; i < screens.count; ++i) { NSRect rect = [screens[i] frame]; if (NSPointInRect(_position.origin, rect)) { _screenRect = rect; @@ -1087,15 +1079,15 @@ - (void)show { windowRect.size = NSMakeSize(insetRectSize.height / theme.scaleFactor, insetRectSize.width / theme.scaleFactor); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { - windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width / theme.scaleFactor : -kOffsetHeight) - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight) + theme.edgeInset.width/theme.scaleFactor; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } // Make the first candidate fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSSize preeditSize = [_view contentRectForRange:_view.preeditRange].size; - windowRect.origin.x += (preeditSize.height + theme.edgeInset.width) / theme.scaleFactor; + windowRect.origin.x += (preeditSize.height + theme.edgeInset.height) / theme.scaleFactor; } } else { windowRect.size = NSMakeSize(insetRectSize.width / theme.scaleFactor, insetRectSize.height / theme.scaleFactor); @@ -1263,13 +1255,12 @@ - (void)showPreedit:(NSString *)preedit // prepare paging and separator for width calculation but no insertion yet NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀\ufe0e" : @"◁\ufe0e").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:theme.linear ? @" %lu " : @"\t%lu\t", pageNum+1] attributes:theme.pagingAttrs]]; -// [paging appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? @" " : @"\t" -// attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷\ufe0e" : @"▶\ufe0e").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀\ufe0e" : @"◁\ufe0e").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:theme.linear ? @" %lu " : @"\t%lu\t", pageNum+1] + attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷\ufe0e" : @"▶\ufe0e").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs]]; fixDefaultFont(paging); CGFloat pagingWidth = theme.showPaging ? NSMaxX([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; @@ -1379,24 +1370,28 @@ - (void)showPreedit:(NSString *)preedit // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); - if (numCandidates && theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (multiLine ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; pagingRange = NSMakeRange(text.length, paging.length); if (theme.linear) { [text appendAttributedString:paging]; - NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - paragraphStyleCandidate.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:floor(_maxTextWidth)]]; - [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; + NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:_maxTextWidth]]; + [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:floor(lineWidth/2)], - [[NSTextTab alloc] initWithType:NSRightTabStopType location:floor(lineWidth)]]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:lineWidth/2], + [[NSTextTab alloc] initWithType:NSRightTabStopType location:lineWidth]]; [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; } } + // extra line fragment will not actually be drawn but ensures the spacing after the last line + if (numCandidates) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; + } + [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1455,8 +1450,7 @@ - (void)hideStatus:(NSTimer *)timer { static inline NSColor *blendColors(NSColor *foregroundColor, NSColor *backgroundColor) { - if (!backgroundColor) { - // return foregroundColor; + if (!backgroundColor) { // return foregroundColor; backgroundColor = [NSColor lightGrayColor]; } return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:backgroundColor] @@ -1549,8 +1543,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; CGFloat borderWidth = [config getDouble:@"style/border_width"]; - CGFloat lineSpacing = fmax([config getDouble:@"style/line_spacing"], hilitedCornerRadius); - CGFloat spacing = fmax([config getDouble:@"style/spacing"], hilitedCornerRadius); + CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; + CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; NSColor *backgroundColor; @@ -1753,8 +1747,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize * scaleFactor]; } } - if (font == nil) { - // use default font + if (font == nil) { // use default font font = [NSFont userFontOfSize:fontSize * scaleFactor]; } NSFontDescriptor *labelFontDescriptor = nil; @@ -1766,7 +1759,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } } if (labelFont == nil) { - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize * scaleFactor weight:NSFontWeightRegular]; + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize * scaleFactor weight:NSFontWeightRegular]; } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1800,7 +1793,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditParagraphStyle.paragraphSpacing = spacing/2 * scaleFactor; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentRight; + pagingParagraphStyle.alignment = NSTextAlignmentLeft; pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left @@ -1837,7 +1830,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); -// pagingAttrs[NSSuperscriptAttributeName] = @(1); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -1887,7 +1879,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); [theme setStatusMessageType:statusMessageType]; - + [theme setAttrs:attrs labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs From a9df665307a2eac701f42074d4221712d5628818 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 4 May 2023 23:33:10 +0200 Subject: [PATCH 063/164] Update SquirrelInputController.m --- SquirrelInputController.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 2253ca1fc..86db139d9 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -523,6 +523,10 @@ -(void)rimeConsumeCommittedText } NSString *substr(const char *str, int length) { + if (str == NULL) { + return @""; + } + length = length > sizeof(str) ? sizeof(str) : length; char substring[length+1]; strncpy(substring, str, length); substring[length] = '\0'; From 2d3b1fe4e5ef972736584ef1f881c823dd8222ff Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 5 May 2023 04:35:19 +0200 Subject: [PATCH 064/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 8abab3397..c5564f9b7 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 8abab3397caaf1a2bea58f46815caee6f3d39667 +Subproject commit c5564f9b7a9e9a2c7a6d0eb3abc38d12b9568e92 From 05b661b49f4a42b04500b32b68153670d3db9f14 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 5 May 2023 06:05:07 +0200 Subject: [PATCH 065/164] fix shaking due to rounding --- SquirrelPanel.m | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 7c8b855ad..94198273c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -647,9 +647,9 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect boundBox = preeditRect; + NSRect innerBox = NSInsetRect(preeditRect, theme.edgeInset.width, theme.edgeInset.height); if (_highlightedIndex != NSNotFound) { - boundBox.size.height -= theme.preeditLinespace/2; + innerBox.size.height -= theme.preeditLinespace/2 - theme.edgeInset.height; } NSRect leadingRect; NSRect bodyRect; @@ -665,10 +665,10 @@ - (void)drawRect:(NSRect)dirtyRect { } else { highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - shrink(highlightedPreeditPoints, boundBox); + shrink(highlightedPreeditPoints, innerBox); highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - shrink(highlightedPreeditPoints2, boundBox); + shrink(highlightedPreeditPoints2, innerBox); [highlightedPreeditPath appendBezierPath:drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } } @@ -889,7 +889,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; - pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize/1.5]; + pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize]; NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; @@ -1388,9 +1388,8 @@ - (void)showPreedit:(NSString *)preedit } // extra line fragment will not actually be drawn but ensures the spacing after the last line - if (numCandidates) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; - } + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; + [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; @@ -1779,22 +1778,22 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFont = [NSFont fontWithName:font.fontName size:commentFontSize * scaleFactor]; } } - NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize/1.5 * scaleFactor]; + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize * scaleFactor]; - CGFloat lineHeight = font.ascender - font.descender; + CGFloat lineHeight = ceil(font.ascender - font.descender); NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; paragraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2 * scaleFactor; - paragraphStyle.lineSpacing = lineSpacing/2 * scaleFactor; + paragraphStyle.minimumLineHeight = lineHeight + ceil(lineSpacing * scaleFactor)/2; + paragraphStyle.lineSpacing = ceil(lineSpacing * scaleFactor)/2; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; preeditParagraphStyle.minimumLineHeight = lineHeight; - preeditParagraphStyle.paragraphSpacing = spacing/2 * scaleFactor; + preeditParagraphStyle.paragraphSpacing = ceil(spacing * scaleFactor)/2; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; pagingParagraphStyle.alignment = NSTextAlignmentLeft; - pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; + pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols @@ -1901,16 +1900,16 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; - borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; + borderHeight = ceil(MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor); + borderWidth = ceil(MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor); NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:cornerRadius * scaleFactor - hilitedCornerRadius:hilitedCornerRadius * scaleFactor + [theme setCornerRadius:ceil(cornerRadius * scaleFactor) + hilitedCornerRadius:ceil(hilitedCornerRadius * scaleFactor) edgeInset:edgeInset borderWidth:MAX(borderHeight, borderWidth) - linespace:lineSpacing * scaleFactor - preeditLinespace:spacing * scaleFactor + linespace:ceil(lineSpacing * scaleFactor) + preeditLinespace:ceil(spacing * scaleFactor) scaleFactor:scaleFactor alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency From 8345cf8407e97a119251953968b5fa0cec9ae8f5 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 5 May 2023 09:22:51 +0200 Subject: [PATCH 066/164] no kerning in status --- SquirrelPanel.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 94198273c..980196eaf 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1423,6 +1423,7 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; + [text addAttribute:NSKernAttributeName value:@(0) range:NSMakeRange(0, text.length)]; fixDefaultFont(text); [text fixAttributesInRange:NSMakeRange(0, text.length)]; From 09e04ec5b12da2e2a48ef832afcbfda360528d86 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 5 May 2023 22:47:12 +0200 Subject: [PATCH 067/164] Change calculation of content bounds --- SquirrelPanel.m | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 980196eaf..dd5f39fcc 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -328,9 +328,13 @@ - (instancetype)initWithFrame:(NSRect)frameRect { // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; - return rect; + [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; + NSRect rect = [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; + NSRect extraRect = [_textView.layoutManager extraLineFragmentRect]; + if (!NSIsEmptyRect(extraRect)) { + rect.size.height -= extraRect.size.height; + } + return NSIntegralRect(rect); } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate @@ -450,13 +454,13 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; + NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { *bodyRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, endPoint.x-startPoint.x, NSHeight(leadingLineRect)); } else { *leadingRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, NSWidth(leadingLineRect)-startPoint.x, NSHeight(leadingLineRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange]; + NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange]; *trailingRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMinY(trailingLineRect)+edgeInset.height, endPoint.x, NSHeight(trailingLineRect)); NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); if (bodyLineRange.length > 0) { @@ -1055,9 +1059,6 @@ - (void)show { } _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); NSRect contentRect = _view.contentRect; - if (contentRect.size.width > textWidth) { - contentRect.size.width = textWidth; - } // in vertical mode, the width and height are interchanged if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || @@ -1782,18 +1783,18 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize * scaleFactor]; CGFloat lineHeight = ceil(font.ascender - font.descender); - NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.minimumLineHeight = lineHeight + ceil(lineSpacing * scaleFactor)/2; - paragraphStyle.lineSpacing = ceil(lineSpacing * scaleFactor)/2; - NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; preeditParagraphStyle.minimumLineHeight = lineHeight; preeditParagraphStyle.paragraphSpacing = ceil(spacing * scaleFactor)/2; + NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + paragraphStyle.alignment = NSTextAlignmentJustified; + paragraphStyle.minimumLineHeight = lineHeight + ceil(lineSpacing * scaleFactor)/2; + paragraphStyle.lineSpacing = ceil(lineSpacing * scaleFactor)/2; + NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentLeft; + pagingParagraphStyle.alignment = NSTextAlignmentCenter; pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left @@ -1869,14 +1870,14 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - attrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - labelAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - highlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - labelHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - commentAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - commentHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - preeditAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); - preeditHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10); + attrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + labelAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + highlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + labelHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + commentAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + commentHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + preeditAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); + preeditHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); [theme setStatusMessageType:statusMessageType]; From f8e82813f81b0f67ea9e22af0d74072c75e2fed0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 6 May 2023 14:32:30 +0200 Subject: [PATCH 068/164] layer --- SquirrelInputController.m | 4 - SquirrelPanel.m | 297 +++++++++++++++++++------------------- data/squirrel.yaml | 1 + 3 files changed, 151 insertions(+), 151 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 86db139d9..2253ca1fc 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -523,10 +523,6 @@ -(void)rimeConsumeCommittedText } NSString *substr(const char *str, int length) { - if (str == NULL) { - return @""; - } - length = length > sizeof(str) ? sizeof(str) : length; char substring[length+1]; strncpy(substring, str, length); substring[length] = '\0'; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index dd5f39fcc..d17c97619 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -73,6 +73,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; @property(nonatomic, readonly) BOOL showPaging; +@property(nonatomic, readonly) BOOL rememberSize; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -115,6 +116,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging + rememberSize:(BOOL)rememberSize linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -196,6 +198,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging + rememberSize:(BOOL)rememberSize linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -209,6 +212,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _alpha = alpha; _translucency = translucency; _showPaging = showPaging; + _rememberSize = rememberSize; _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; @@ -329,12 +333,14 @@ - (instancetype)initWithFrame:(NSRect)frameRect { // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; + NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; NSRect rect = [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; - NSRect extraRect = [_textView.layoutManager extraLineFragmentRect]; - if (!NSIsEmptyRect(extraRect)) { - rect.size.height -= extraRect.size.height; - } - return NSIntegralRect(rect); + NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL withoutAdditionalLayout:YES]; + // integral the size of the window rect to avoid pixel jumping + CGFloat scaleFactor = self.currentTheme.scaleFactor; + rect.size.height = ceil((NSMaxY(finalLineRect) - rect.origin.y) / scaleFactor) * scaleFactor; + rect.size.width = ceil(rect.size.width / scaleFactor) * scaleFactor;; + return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate @@ -382,11 +388,11 @@ - (void) drawViewWith:(NSArray*)candidateRanges NSPoint control1; NSPoint control2; NSPoint target = previousPoint; - NSPoint diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { - target.x += sign(diff.x/beta)*beta; + CGVector diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); + if (ABS(diff.dx) >= ABS(diff.dy)) { + target.x += sign(diff.dx/beta)*beta; } else { - target.y += sign(diff.y/beta)*beta; + target.y += sign(diff.dy/beta)*beta; } [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; i += 1) { @@ -395,24 +401,24 @@ - (void) drawViewWith:(NSArray*)candidateRanges nextPoint = (vertex[(i+1)%vertex.count]).pointValue; target = point; control1 = point; - diff = NSMakePoint(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.x) >= ABS(diff.y)) { - target.x -= sign(diff.x/beta)*beta; - control1.x -= sign(diff.x/beta)*alpha; + diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); + if (ABS(diff.dx) >= ABS(diff.dy)) { + target.x -= sign(diff.dx/beta)*beta; + control1.x -= sign(diff.dx/beta)*alpha; } else { - target.y -= sign(diff.y/beta)*beta; - control1.y -= sign(diff.y/beta)*alpha; + target.y -= sign(diff.dy/beta)*beta; + control1.y -= sign(diff.dy/beta)*alpha; } [path lineToPoint:target]; target = point; control2 = point; - diff = NSMakePoint(nextPoint.x - point.x, nextPoint.y - point.y); - if (ABS(diff.x) > ABS(diff.y)) { - control2.x += sign(diff.x/beta)*alpha; - target.x += sign(diff.x/beta)*beta; + diff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); + if (ABS(diff.dx) > ABS(diff.dy)) { + control2.x += sign(diff.dx/beta)*alpha; + target.x += sign(diff.dx/beta)*beta; } else { - control2.y += sign(diff.y/beta)*alpha; - target.y += sign(diff.y/beta)*beta; + control2.y += sign(diff.dy/beta)*alpha; + target.y += sign(diff.dy/beta)*beta; } [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; } @@ -421,19 +427,17 @@ - (void) drawViewWith:(NSArray*)candidateRanges } NSArray *rectVertex(NSRect rect) { - return @[ - @(rect.origin), - @(NSMakePoint(rect.origin.x, rect.origin.y+rect.size.height)), - @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y+rect.size.height)), - @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y)) - ]; + return @[@(rect.origin), + @(NSMakePoint(rect.origin.x, rect.origin.y+rect.size.height)), + @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y+rect.size.height)), + @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y))]; } -void xyTranslation(NSMutableArray *shape, NSPoint direction) { +void xyTranslation(NSMutableArray *shape, CGFloat dx, CGFloat dy) { for (NSUInteger i = 0; i < shape.count; i += 1) { NSPoint point = (shape[i]).pointValue; - point.x += direction.x; - point.y += direction.y; + point.x += dx; + point.y += dy; shape[i] = @(point); } } @@ -547,7 +551,7 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *candidateBlockPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; - SquirrelTheme * theme = self.currentTheme; + SquirrelTheme *theme = self.currentTheme; [NSBezierPath setDefaultLineWidth:0]; NSRect backgroundRect = dirtyRect; @@ -558,10 +562,10 @@ - (void)drawRect:(NSRect)dirtyRect { if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; preeditRect.size.width = backgroundRect.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2; + preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2.0; preeditRect.origin = backgroundRect.origin; if (_highlightedIndex == NSNotFound) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; + preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2.0; } } @@ -601,7 +605,7 @@ - (void)drawRect:(NSRect)dirtyRect { if (_pagingRange.length == 0 || theme.linear) { outerBox.size.height -= theme.edgeInset.height; } - NSRect innerBox = NSInsetRect(outerBox, 1, theme.linespace/2+1); + NSRect innerBox = NSInsetRect(outerBox, 0, theme.linespace/2.0); if (theme.preeditBackgroundColor != nil) { candidateBlockPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } @@ -653,7 +657,7 @@ - (void)drawRect:(NSRect)dirtyRect { if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { NSRect innerBox = NSInsetRect(preeditRect, theme.edgeInset.width, theme.edgeInset.height); if (_highlightedIndex != NSNotFound) { - innerBox.size.height -= theme.preeditLinespace/2 - theme.edgeInset.height; + innerBox.size.height -= theme.preeditLinespace/2.0 - theme.edgeInset.height; } NSRect leadingRect; NSRect bodyRect; @@ -683,86 +687,77 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); borderPath.lineWidth = theme.borderWidth; - // Nothing should extend beyond backgroundPath - [backgroundPath addClip]; - // This block of code enables independent transparencies in highlighted colour and background colour. - // Disabled because of the flaw: edges or rounded corners of the heighlighted area are rendered with undesirable shadows. -#if 0 - // Calculate intersections. - if (![highlightedPath isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath copy]]; - if (![highlightedPath2 isEmpty]) { - [backgroundPath appendBezierPath:[highlightedPath2 copy]]; - } - } - - if (![preeditPath isEmpty]) { - [backgroundPath appendBezierPath:[preeditPath copy]]; - } - - if (![highlightedPreeditPath isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath copy]]; - } - if (![highlightedPreeditPath2 isEmpty]) { - if (preeditPath != nil) { - [preeditPath appendBezierPath:[highlightedPreeditPath2 copy]]; - } else { - [backgroundPath appendBezierPath:[highlightedPreeditPath2 copy]]; - } - } - } - [backgroundPath setWindingRule:NSEvenOddWindingRule]; - [preeditPath setWindingRule:NSEvenOddWindingRule]; -#endif + // set layers + [self.layer setSublayers: NULL]; + CAShapeLayer *maskLayer = [CAShapeLayer layer]; + maskLayer.path = [backgroundPath quartzPath]; if (theme.backgroundImage) { - [theme.backgroundImage setFill]; - [backgroundPath fill]; + self.layer.backgroundColor = [theme.backgroundImage CGColor]; } - [theme.backgroundColor setFill]; - [backgroundPath fill]; + CAShapeLayer *panelLayer = [CAShapeLayer layer]; + panelLayer.path = [backgroundPath quartzPath]; + panelLayer.fillColor = [theme.backgroundColor CGColor]; + [self.layer addSublayer:panelLayer]; if (theme.preeditBackgroundColor && (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { + if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { + CAShapeLayer *highlightedLayer = [CAShapeLayer layer]; + highlightedLayer.path = [highlightedPath quartzPath]; + highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; + [panelLayer addSublayer:highlightedLayer]; + } nonCandidateBlockPath = [backgroundPath copy]; if (![candidateBlockPath isEmpty]) { - [nonCandidateBlockPath appendBezierPath:[candidateBlockPath bezierPathByReversingPath]]; - [nonCandidateBlockPath setWindingRule:NSNonZeroWindingRule]; + [nonCandidateBlockPath appendBezierPath:candidateBlockPath]; + [nonCandidateBlockPath setWindingRule:NSEvenOddWindingRule]; } - [theme.preeditBackgroundColor setFill]; - [nonCandidateBlockPath fill]; - } - if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { - [theme.highlightedStripColor setFill]; - [highlightedPath fill]; + CAShapeLayer *nonCandidateLayer = [CAShapeLayer layer]; + nonCandidateLayer.path = [nonCandidateBlockPath quartzPath]; + nonCandidateLayer.fillRule = kCAFillRuleEvenOdd; + nonCandidateLayer.fillColor = [theme.preeditBackgroundColor CGColor]; + [panelLayer addSublayer:nonCandidateLayer]; } if (theme.highlightedPreeditColor) { - if (_pagingButton == NSPageUpFunctionKey) { - [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; - [pageUpPath fill]; - } if (_pagingButton == NSBeginFunctionKey) { - [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; - [pageUpPath fill]; - } else if (_pagingButton == NSPageDownFunctionKey) { - [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] setFill]; - [pageDownPath fill]; - } else if (_pagingButton == NSEndFunctionKey) { - [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] setFill]; - [pageDownPath fill]; + if (_pagingRange.length > 0) { + CAShapeLayer *pageUpLayer = [CAShapeLayer layer]; + pageUpLayer.path = [pageUpPath quartzPath]; + pageUpLayer.fillColor = nil; + CAShapeLayer *pageDownLayer = [CAShapeLayer layer]; + pageDownLayer.path = [pageDownPath quartzPath]; + pageDownLayer.fillColor = nil; + if (_pagingButton == NSPageUpFunctionKey) { + pageUpLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; + } if (_pagingButton == NSBeginFunctionKey) { + pageUpLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; + } else if (_pagingButton == NSPageDownFunctionKey) { + pageDownLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; + } else if (_pagingButton == NSEndFunctionKey) { + pageDownLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; + } + [panelLayer addSublayer:pageUpLayer]; + [panelLayer addSublayer:pageDownLayer]; } if(![highlightedPreeditPath isEmpty]) { - [theme.highlightedPreeditColor setFill]; - [highlightedPreeditPath fill]; + CAShapeLayer *highlightedPreeditLayer = [CAShapeLayer layer]; + highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; + highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; + [panelLayer addSublayer:highlightedPreeditLayer]; } } if (theme.borderColor && (theme.borderWidth > 0)) { - [theme.borderColor setStroke]; - [borderPath stroke]; + CAShapeLayer *borderLayer = [CAShapeLayer layer]; + borderLayer.path = [borderPath quartzPath]; + borderLayer.lineWidth = theme.borderWidth; + borderLayer.fillColor = nil; + borderLayer.strokeColor = [theme.borderColor CGColor]; + borderLayer.mask = maskLayer; + [panelLayer addSublayer:borderLayer]; } [_textView setTextContainerInset:theme.edgeInset]; + [self.layer setShouldRasterize:YES]; + [self.layer setRasterizationScale:theme.scaleFactor]; } - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { @@ -830,6 +825,10 @@ - (BOOL)showPaging { return _view.currentTheme.showPaging; } +- (BOOL)rememberSize { + return _view.currentTheme.rememberSize; +} + void fixDefaultFont(NSMutableAttributedString *text) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); @@ -915,7 +914,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:NSWindowStyleMaskNonactivatingPanel + styleMask:(NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless) backing:NSBackingStoreBuffered defer:YES]; if (self) { @@ -1058,11 +1057,11 @@ - (void)show { textWidth = _maxTextWidth; } _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); - NSRect contentRect = _view.contentRect; // in vertical mode, the width and height are interchanged - if ((theme.vertical && NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) || - (!theme.vertical && NSMinX(_position)+(MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2)/theme.scaleFactor > NSMaxX(_screenRect))) { + NSRect contentRect = _view.contentRect; + if (theme.rememberSize && (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : + ((NSMinX(_position) + (MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2)/theme.scaleFactor > NSMaxX(_screenRect))))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { @@ -1074,24 +1073,24 @@ - (void)show { // the sweep direction of the client app changes the behavior of adjusting squirrel panel position NSRect windowRect; bool sweepVertical = NSWidth(_position) > NSHeight(_position); - NSSize insetRectSize = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, - contentRect.size.height + theme.edgeInset.height * 2); + NSSize scaledRectSize = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, + contentRect.size.height + theme.edgeInset.height * 2); if (theme.vertical) { - windowRect.size = NSMakeSize(insetRectSize.height / theme.scaleFactor, insetRectSize.width / theme.scaleFactor); + windowRect.size = NSMakeSize(scaledRectSize.height / theme.scaleFactor, scaledRectSize.width / theme.scaleFactor); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width / theme.scaleFactor : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } - // Make the first candidate fixed at the left of cursor + // Make the right edge of candidate block fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { - NSSize preeditSize = [_view contentRectForRange:_view.preeditRange].size; - windowRect.origin.x += (preeditSize.height + theme.edgeInset.height) / theme.scaleFactor; + CGFloat preeditHeight = NSHeight([_view contentRectForRange:_view.preeditRange]); + windowRect.origin.x += (preeditHeight + theme.edgeInset.height) / theme.scaleFactor; } } else { - windowRect.size = NSMakeSize(insetRectSize.width / theme.scaleFactor, insetRectSize.height / theme.scaleFactor); + windowRect.size = NSMakeSize(scaledRectSize.width / theme.scaleFactor, scaledRectSize.height / theme.scaleFactor); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { @@ -1118,24 +1117,23 @@ - (void)show { if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); } + [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { self.contentView.boundsRotation = -90.0; - _view.textView.boundsRotation = 0; - [self.contentView setBoundsSize:NSMakeSize(insetRectSize.height, insetRectSize.width)]; - [self.contentView setBoundsOrigin:NSMakePoint(0, windowRect.size.width)]; - [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; + [self.contentView setBoundsSize:NSMakeSize(scaledRectSize.height, scaledRectSize.width)]; + [self.contentView setBoundsOrigin:NSMakePoint(0.0, windowRect.size.width)]; } else { - self.contentView.boundsRotation = 0; - _view.textView.boundsRotation = 0; - [self.contentView setBoundsSize:NSMakeSize(insetRectSize.width, insetRectSize.height)]; - [self.contentView setBoundsOrigin:NSMakePoint(0, 0)]; - [_view.textView setBoundsOrigin:NSMakePoint(0, 0)]; + self.contentView.boundsRotation = 0.0; + [self.contentView setBoundsSize:NSMakeSize(scaledRectSize.width, scaledRectSize.height)]; + [self.contentView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; } [_view setFrame:self.contentView.bounds]; [_view.textView setFrame:self.contentView.bounds]; + _view.textView.boundsRotation = 0.0; + [_view.textView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; BOOL translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency) { @@ -1219,6 +1217,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); + CGFloat preeditWidth = 0.0; // preedit if (preedit) { NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; @@ -1247,6 +1246,7 @@ - (void)showPreedit:(NSString *)preedit [text appendAttributedString:preeditLine]; preeditRange = NSMakeRange(0, text.length); + preeditWidth = NSWidth([preeditLine boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); if (numCandidates) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; @@ -1267,6 +1267,7 @@ - (void)showPreedit:(NSString *)preedit CGFloat separatorWidth = theme.linear ? NSMaxX([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; CGFloat lineWidth = 0.0 - separatorWidth; + CGFloat maxLineWidth = MIN(preeditWidth, _maxTextWidth); BOOL multiLine = NO; // candidates @@ -1348,13 +1349,14 @@ - (void)showPreedit:(NSString *)preedit if (lineWidth + separatorWidth + candidateWidth > _maxTextWidth) { separtatorString = @"\u2028"; multiLine = YES; + maxLineWidth = MAX(maxLineWidth, lineWidth); lineWidth = candidateWidth; } else { separtatorString = @" "; lineWidth += separatorWidth + candidateWidth; } } else { - lineWidth = MAX(lineWidth, MIN(candidateWidth, _maxTextWidth)); + maxLineWidth = MAX(maxLineWidth, MIN(candidateWidth, _maxTextWidth)); paragraphStyleCandidate.headIndent = labelWidth; } NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; @@ -1374,23 +1376,25 @@ - (void)showPreedit:(NSString *)preedit if (numCandidates && theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (multiLine ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; pagingRange = NSMakeRange(text.length, paging.length); + if (theme.rememberSize) { + maxLineWidth = MAX(maxLineWidth, _maxHeight); + } if (theme.linear) { [text appendAttributedString:paging]; NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:_maxTextWidth]]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:lineWidth/2], - [[NSTextTab alloc] initWithType:NSRightTabStopType location:lineWidth]]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], + [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; } } // extra line fragment will not actually be drawn but ensures the spacing after the last line - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; - + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; @@ -1530,6 +1534,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; BOOL translucency = [config getBool:@"style/translucency"]; BOOL showPaging = [config getBool:@"style/show_paging"]; + BOOL rememberSize = [config getBool:@"style/remember_size"]; NSString *statusMessageType = [config getString:@"style/status_message_type"]; NSString *candidateFormat = [config getString:@"style/candidate_format"]; @@ -1782,20 +1787,26 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo } NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize * scaleFactor]; - CGFloat lineHeight = ceil(font.ascender - font.descender); + CGFloat fontLineHeight = MAX(font.ascender - font.descender, [NSFont systemFontOfSize:fontSize].ascender - [NSFont systemFontOfSize:fontSize].descender); +// fontLineHeight = ceil(fontLineHeight / scaleFactor) * scaleFactor; + CGFloat commentLineHeight = MAX(commentFont.ascender - commentFont.descender, [NSFont systemFontOfSize:commentFontSize].ascender - [NSFont systemFontOfSize:commentFontSize].descender); +// commentLineHeight = ceil(commentLineHeight / scaleFactor) * scaleFactor; + CGFloat labelLineHeight = MAX(labelFont.ascender - labelFont.descender, [NSFont systemFontOfSize:labelFontSize].ascender - [NSFont systemFontOfSize:labelFontSize].descender); +// labelLineHeight = ceil(labelLineHeight / scaleFactor) * scaleFactor; + CGFloat lineHeight = MAX(fontLineHeight, MAX(commentLineHeight, labelLineHeight)); + NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; - preeditParagraphStyle.minimumLineHeight = lineHeight; - preeditParagraphStyle.paragraphSpacing = ceil(spacing * scaleFactor)/2; + preeditParagraphStyle.minimumLineHeight = fontLineHeight; + preeditParagraphStyle.paragraphSpacing = spacing/2.0 * scaleFactor; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.alignment = NSTextAlignmentJustified; - paragraphStyle.minimumLineHeight = lineHeight + ceil(lineSpacing * scaleFactor)/2; - paragraphStyle.lineSpacing = ceil(lineSpacing * scaleFactor)/2; + paragraphStyle.alignment = NSTextAlignmentLeft; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2.0 * scaleFactor; + paragraphStyle.lineSpacing = lineSpacing/2.0 * scaleFactor; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentCenter; - pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); + pagingParagraphStyle.alignment = NSTextAlignmentLeft; // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols @@ -1830,7 +1841,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor - ceil(labelFontSize/5.0)); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; @@ -1870,15 +1881,6 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - attrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - labelAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - highlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - labelHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - commentAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - commentHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - preeditAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - preeditHighlightedAttrs[NSKernAttributeName] = @(fontSize * scaleFactor / 10.0); - [theme setStatusMessageType:statusMessageType]; [theme setAttrs:attrs @@ -1902,20 +1904,21 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - borderHeight = ceil(MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor); - borderWidth = ceil(MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor); + borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; + borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:ceil(cornerRadius * scaleFactor) - hilitedCornerRadius:ceil(hilitedCornerRadius * scaleFactor) + [theme setCornerRadius:cornerRadius * scaleFactor + hilitedCornerRadius:hilitedCornerRadius * scaleFactor edgeInset:edgeInset borderWidth:MAX(borderHeight, borderWidth) - linespace:ceil(lineSpacing * scaleFactor) - preeditLinespace:ceil(spacing * scaleFactor) + linespace:lineSpacing * scaleFactor + preeditLinespace:spacing * scaleFactor scaleFactor:scaleFactor alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency showPaging:showPaging + rememberSize:rememberSize linear:linear vertical:vertical inlinePreedit:inlinePreedit diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 620cd048e..52e850ea7 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -29,6 +29,7 @@ style: text_orientation: horizontal # horizontal | vertical inline_preedit: true show_paging : true + remember_size: true corner_radius: 10 hilited_corner_radius: 0 From df11466a824c095d6c4f904ad77ef82d4622c8c7 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 6 May 2023 14:32:30 +0200 Subject: [PATCH 069/164] layer --- SquirrelPanel.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d17c97619..3952a8571 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -338,8 +338,8 @@ - (NSRect)contentRect { NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL withoutAdditionalLayout:YES]; // integral the size of the window rect to avoid pixel jumping CGFloat scaleFactor = self.currentTheme.scaleFactor; - rect.size.height = ceil((NSMaxY(finalLineRect) - rect.origin.y) / scaleFactor) * scaleFactor; - rect.size.width = ceil(rect.size.width / scaleFactor) * scaleFactor;; + rect.size.height = round((NSMaxY(finalLineRect) - rect.origin.y) / scaleFactor) * scaleFactor; + rect.size.width = round(rect.size.width / scaleFactor) * scaleFactor;; return rect; } @@ -1788,11 +1788,8 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize * scaleFactor]; CGFloat fontLineHeight = MAX(font.ascender - font.descender, [NSFont systemFontOfSize:fontSize].ascender - [NSFont systemFontOfSize:fontSize].descender); -// fontLineHeight = ceil(fontLineHeight / scaleFactor) * scaleFactor; CGFloat commentLineHeight = MAX(commentFont.ascender - commentFont.descender, [NSFont systemFontOfSize:commentFontSize].ascender - [NSFont systemFontOfSize:commentFontSize].descender); -// commentLineHeight = ceil(commentLineHeight / scaleFactor) * scaleFactor; CGFloat labelLineHeight = MAX(labelFont.ascender - labelFont.descender, [NSFont systemFontOfSize:labelFontSize].ascender - [NSFont systemFontOfSize:labelFontSize].descender); -// labelLineHeight = ceil(labelLineHeight / scaleFactor) * scaleFactor; CGFloat lineHeight = MAX(fontLineHeight, MAX(commentLineHeight, labelLineHeight)); NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; From ff2fe332a0bec0c3b8e6b8ed93b4d1bd2e648f4c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 9 May 2023 14:53:48 +0200 Subject: [PATCH 070/164] revert scale & rename function --- SquirrelConfig.h | 2 +- SquirrelConfig.m | 24 +-- SquirrelPanel.m | 387 +++++++++++++++++++++++------------------------ 3 files changed, 201 insertions(+), 212 deletions(-) diff --git a/SquirrelConfig.h b/SquirrelConfig.h index ff397ed67..2a6f4519d 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -27,7 +27,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; // 0xaabbggrr or 0xbbggrr - (NSColor *)getColor:(NSString *)option; // file path (absolute or relative to ~/Library/Rime) -- (NSColor *)getImage:(NSString *)option; +- (NSColor *)getPattern:(NSString *)option; - (SquirrelAppOptions *)getAppOptions:(NSString *)appName; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index efd9cd3db..1a7e81c20 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -137,17 +137,17 @@ - (NSColor *)getColor:(NSString *)option { return [_baseConfig getColor:option]; } -- (NSColor *)getImage:(NSString *)option { +- (NSColor *)getPattern:(NSString *)option { NSColor *cachedValue = [self cachedValueOfClass:[NSColor class] forKey:option]; if (cachedValue) { return cachedValue; } - NSColor *color = [self colorFromImage:[self getString:option]]; - if (color) { - _cache[option] = color; - return color; + NSColor *pattern = [self patternFromFile:[self getString:option]]; + if (pattern) { + _cache[option] = pattern; + return pattern; } - return [_baseConfig getImage:option]; + return [_baseConfig getPattern:option]; } - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { @@ -200,16 +200,16 @@ - (NSColor *)colorFromString:(NSString *)string { } } -- (NSColor *)colorFromImage:(NSString *)image { - if (image == nil) { +- (NSColor *)patternFromFile:(NSString *)filePath { + if (filePath == nil) { return nil; } NSFileManager* fileManager = [NSFileManager defaultManager]; [fileManager changeCurrentDirectoryPath:[@"~/Library/Rime" stringByStandardizingPath]]; - NSString *imageFile = [image stringByStandardizingPath]; - if ([fileManager fileExistsAtPath:imageFile]) { - NSColor *color = [NSColor colorWithPatternImage:[[NSImage alloc] initWithContentsOfFile:imageFile]]; - return color; + NSString *patternFile = [filePath stringByStandardizingPath]; + if ([fileManager fileExistsAtPath:patternFile]) { + NSColor *pattern = [NSColor colorWithPatternImage:[[NSImage alloc] initWithContentsOfFile:patternFile]]; + return pattern; } return nil; } diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 3952a8571..9e766de17 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -69,7 +69,6 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; -@property(nonatomic, readonly) CGFloat scaleFactor; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) BOOL translucency; @property(nonatomic, readonly) BOOL showPaging; @@ -112,7 +111,6 @@ - (void)setCornerRadius:(CGFloat)cornerRadius borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace - scaleFactor:(CGFloat)scaleFactor alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging @@ -194,7 +192,6 @@ - (void)setCornerRadius:(CGFloat)cornerRadius borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace - scaleFactor:(CGFloat)scaleFactor alpha:(CGFloat)alpha translucency:(BOOL)translucency showPaging:(BOOL)showPaging @@ -208,7 +205,6 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _edgeInset = edgeInset; _borderWidth = borderWidth; _linespace = linespace; - _scaleFactor = scaleFactor; _alpha = alpha; _translucency = translucency; _showPaging = showPaging; @@ -336,10 +332,7 @@ - (NSRect)contentRect { NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; NSRect rect = [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL withoutAdditionalLayout:YES]; - // integral the size of the window rect to avoid pixel jumping - CGFloat scaleFactor = self.currentTheme.scaleFactor; - rect.size.height = round((NSMaxY(finalLineRect) - rect.origin.y) / scaleFactor) * scaleFactor; - rect.size.width = round(rect.size.width / scaleFactor) * scaleFactor;; + rect.size.height = NSMaxY(finalLineRect) - NSMinY(rect); return rect; } @@ -428,9 +421,9 @@ - (void) drawViewWith:(NSArray*)candidateRanges NSArray *rectVertex(NSRect rect) { return @[@(rect.origin), - @(NSMakePoint(rect.origin.x, rect.origin.y+rect.size.height)), - @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y+rect.size.height)), - @(NSMakePoint(rect.origin.x+rect.size.width, rect.origin.y))]; + @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), + @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), + @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y))]; } void xyTranslation(NSMutableArray *shape, CGFloat dx, CGFloat dy) { @@ -457,18 +450,27 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; + NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - *bodyRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, endPoint.x-startPoint.x, NSHeight(leadingLineRect)); + *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + endPoint.x - startPoint.x, NSHeight(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect)+startPoint.x+edgeInset.width, NSMinY(leadingLineRect)+edgeInset.height, NSWidth(leadingLineRect)-startPoint.x, NSHeight(leadingLineRect)); + CGFloat rightEdge = MAX(NSMaxX(leadingLineRect), NSMaxX(boundingRect)); + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange]; - *trailingRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMinY(trailingLineRect)+edgeInset.height, endPoint.x, NSHeight(trailingLineRect)); + CGFloat leftEdge = MIN(NSMinX(trailingLineRect), NSMinX(boundingRect)); + *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, + NSMinX(trailingLineRect) - leftEdge + endPoint.x, NSHeight(trailingLineRect)); NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); if (bodyLineRange.length > 0) { - *bodyRect = NSMakeRect(NSMinX(trailingLineRect)+edgeInset.width, NSMaxY(leadingLineRect)+edgeInset.height, NSMaxX(leadingLineRect)-NSMinX(trailingLineRect), NSMinY(trailingLineRect)-NSMaxY(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } } @@ -562,10 +564,10 @@ - (void)drawRect:(NSRect)dirtyRect { if (_preeditRange.length > 0) { preeditRect = [self contentRectForRange:_preeditRange]; preeditRect.size.width = backgroundRect.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2.0; + preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2; preeditRect.origin = backgroundRect.origin; if (_highlightedIndex == NSNotFound) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2.0; + preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; } } @@ -605,11 +607,12 @@ - (void)drawRect:(NSRect)dirtyRect { if (_pagingRange.length == 0 || theme.linear) { outerBox.size.height -= theme.edgeInset.height; } - NSRect innerBox = NSInsetRect(outerBox, 0, theme.linespace/2.0); + NSRect innerBox = NSInsetRect(outerBox, 0, theme.linespace/2); if (theme.preeditBackgroundColor != nil) { candidateBlockPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } + // Draw candidate highlight rect if (theme.linear) { for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { NSRange candidateRange = _candidateRanges[i].rangeValue; @@ -622,14 +625,16 @@ - (void)drawRect:(NSRect)dirtyRect { NSMutableArray *candidatePoints2; // Handles the special case where containing boxes are separated if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePoints = [rectVertex(leadingRect) copy]; - candidatePoints2 = [rectVertex(trailingRect) copy]; + candidatePoints = [rectVertex(leadingRect) mutableCopy]; + candidatePoints2 = [rectVertex(trailingRect) mutableCopy]; } else { - candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) copy]; + candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } + expand(candidatePoints, innerBox, outerBox); NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (candidatePoints2.count > 0) { + expand(candidatePoints2, innerBox, outerBox); [candidatePath appendBezierPath:drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } _candidatePaths[i] = candidatePath; @@ -642,8 +647,8 @@ - (void)drawRect:(NSRect)dirtyRect { candidateRect.origin.x = textField.origin.x; candidateRect.origin.y += theme.edgeInset.height; NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; - expand(candidatePoints, innerBox, outerBox); + expand(candidatePoints, innerBox, outerBox); NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; } @@ -657,7 +662,7 @@ - (void)drawRect:(NSRect)dirtyRect { if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { NSRect innerBox = NSInsetRect(preeditRect, theme.edgeInset.width, theme.edgeInset.height); if (_highlightedIndex != NSNotFound) { - innerBox.size.height -= theme.preeditLinespace/2.0 - theme.edgeInset.height; + innerBox.size.height -= theme.preeditLinespace/2 - theme.edgeInset.height; } NSRect leadingRect; NSRect bodyRect; @@ -681,49 +686,51 @@ - (void)drawRect:(NSRect)dirtyRect { } } - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); - _shape.path = [backgroundPath quartzPath]; - NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); borderPath.lineWidth = theme.borderWidth; + backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); // set layers + _shape.path = [backgroundPath quartzPath]; + self.layer.contentsFormat = kCAContentsFormatRGBA16Float; [self.layer setSublayers: NULL]; - CAShapeLayer *maskLayer = [CAShapeLayer layer]; - maskLayer.path = [backgroundPath quartzPath]; + self.layer.cornerRadius = theme.cornerRadius; + if (@available(macOS 10.15, *)) { + self.layer.cornerCurve = kCACornerCurveContinuous; + } if (theme.backgroundImage) { self.layer.backgroundColor = [theme.backgroundImage CGColor]; } - CAShapeLayer *panelLayer = [CAShapeLayer layer]; + CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; panelLayer.path = [backgroundPath quartzPath]; panelLayer.fillColor = [theme.backgroundColor CGColor]; [self.layer addSublayer:panelLayer]; if (theme.preeditBackgroundColor && (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { - CAShapeLayer *highlightedLayer = [CAShapeLayer layer]; + CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; highlightedLayer.path = [highlightedPath quartzPath]; highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; [panelLayer addSublayer:highlightedLayer]; } + CAShapeLayer *nonCandidateLayer = [[CAShapeLayer alloc] init]; nonCandidateBlockPath = [backgroundPath copy]; if (![candidateBlockPath isEmpty]) { [nonCandidateBlockPath appendBezierPath:candidateBlockPath]; [nonCandidateBlockPath setWindingRule:NSEvenOddWindingRule]; + nonCandidateLayer.fillRule = kCAFillRuleEvenOdd; } - CAShapeLayer *nonCandidateLayer = [CAShapeLayer layer]; nonCandidateLayer.path = [nonCandidateBlockPath quartzPath]; - nonCandidateLayer.fillRule = kCAFillRuleEvenOdd; nonCandidateLayer.fillColor = [theme.preeditBackgroundColor CGColor]; [panelLayer addSublayer:nonCandidateLayer]; } if (theme.highlightedPreeditColor) { if (_pagingRange.length > 0) { - CAShapeLayer *pageUpLayer = [CAShapeLayer layer]; + CAShapeLayer *pageUpLayer = [[CAShapeLayer alloc] init]; pageUpLayer.path = [pageUpPath quartzPath]; pageUpLayer.fillColor = nil; - CAShapeLayer *pageDownLayer = [CAShapeLayer layer]; + CAShapeLayer *pageDownLayer = [[CAShapeLayer alloc] init]; pageDownLayer.path = [pageDownPath quartzPath]; pageDownLayer.fillColor = nil; if (_pagingButton == NSPageUpFunctionKey) { @@ -739,25 +746,20 @@ - (void)drawRect:(NSRect)dirtyRect { [panelLayer addSublayer:pageDownLayer]; } if(![highlightedPreeditPath isEmpty]) { - CAShapeLayer *highlightedPreeditLayer = [CAShapeLayer layer]; + CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; [panelLayer addSublayer:highlightedPreeditLayer]; } } if (theme.borderColor && (theme.borderWidth > 0)) { - CAShapeLayer *borderLayer = [CAShapeLayer layer]; + CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; borderLayer.path = [borderPath quartzPath]; borderLayer.lineWidth = theme.borderWidth; borderLayer.fillColor = nil; borderLayer.strokeColor = [theme.borderColor CGColor]; - borderLayer.mask = maskLayer; [panelLayer addSublayer:borderLayer]; } - - [_textView setTextContainerInset:theme.edgeInset]; - [self.layer setShouldRasterize:YES]; - [self.layer setRasterizationScale:theme.scaleFactor]; } - (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { @@ -829,7 +831,7 @@ - (BOOL)rememberSize { return _view.currentTheme.rememberSize; } -void fixDefaultFont(NSMutableAttributedString *text) { +void fixDefaultFont(NSMutableAttributedString *text, BOOL vertical) { [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; NSRange currentFontRange = NSMakeRange(NSNotFound, 0); NSUInteger i = 0; @@ -838,6 +840,18 @@ void fixDefaultFont(NSMutableAttributedString *text) { if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; + if (charFont.pointSize < 24 && vertical) { + NSRect const emojiRect = [defaultFont boundingRectForGlyph:[defaultFont glyphWithName:@"u2B1B"]]; // ⬛ + NSRect const defaultRect = [defaultFont boundingRectForGlyph:[defaultFont glyphWithName:@"uni25A0"]]; // ■ + [text addAttribute:NSBaselineOffsetAttributeName value:@((ceil(NSMaxY(defaultRect)) - ceil(NSMaxY(emojiRect)))/2) range:currentFontRange]; + if (currentFontRange.location == 0) { + NSMutableDictionary *attrs = [[text attributesAtIndex:0 effectiveRange:NULL] mutableCopy]; + attrs[NSKernAttributeName] = @(ceil(NSMaxY(emojiRect) - NSMaxY(defaultRect))); + [text insertAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:attrs] atIndex:0]; + } else { + [text addAttribute:NSKernAttributeName value:@(ceil(NSMaxY(emojiRect) - NSMaxY(defaultRect))) range:NSMakeRange(currentFontRange.location-1, 1)]; + } + } } i = NSMaxRange(currentFontRange); } @@ -894,8 +908,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize]; - NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; + NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; NSParagraphStyle *pagingParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; [theme setAttrs:attrs @@ -1019,18 +1033,18 @@ - (void)sendEvent:(NSEvent *)event { - (void)getCurrentScreen { // get current screen - _screenRect = [NSScreen mainScreen].frame; + NSScreen *currentScreen = [NSScreen mainScreen]; + _screenRect = [currentScreen visibleFrame]; NSArray *screens = [NSScreen screens]; for (NSUInteger i = 0; i < screens.count; ++i) { - NSRect rect = [screens[i] frame]; - if (NSPointInRect(_position.origin, rect)) { - _screenRect = rect; + if (NSPointInRect(_position.origin, [screens[i] frame])) { + _screenRect = [screens[i] visibleFrame]; break; } } } -// Get the window size, its bounds (insetRect) will be the dirtyRect in SquirrelView.drawRect +// Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { [self getCurrentScreen]; SquirrelTheme *theme = _view.currentTheme; @@ -1045,62 +1059,58 @@ - (void)show { //Break line if the text is too long, based on screen size. CGFloat textWidth = _view.textView.textStorage.size.width; NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12.0 / theme.scaleFactor; + CGFloat fontScale = currentFont.pointSize / 12.0; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12.0); - _maxTextWidth = theme.vertical - ? NSHeight(_screenRect) * textWidthRatio * theme.scaleFactor - theme.edgeInset.height * 2 - : NSWidth(_screenRect) * textWidthRatio * theme.scaleFactor - theme.edgeInset.width * 2; - CGFloat maxTextHeight = theme.vertical - ? NSWidth(_screenRect) * theme.scaleFactor - theme.edgeInset.width * 2 - : NSHeight(_screenRect) * theme.scaleFactor - theme.edgeInset.height * 2; + _maxTextWidth = theme.vertical ? (NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2) : (NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2); + CGFloat maxTextHeight = theme.vertical ? (NSWidth(_screenRect) - theme.edgeInset.width * 2) : (NSHeight(_screenRect) - theme.edgeInset.height * 2); if (textWidth > _maxTextWidth) { textWidth = _maxTextWidth; } - _view.textView.textContainer.containerSize = NSMakeSize(textWidth, maxTextHeight); + _view.textView.textContainer.size = NSMakeSize(textWidth, maxTextHeight); // in vertical mode, the width and height are interchanged NSRect contentRect = _view.contentRect; if (theme.rememberSize && (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : - ((NSMinX(_position) + (MAX(contentRect.size.width, _maxHeight)+theme.edgeInset.width*2)/theme.scaleFactor > NSMaxX(_screenRect))))) { + ((NSMinX(_position) + MAX(contentRect.size.width, _maxHeight) + theme.edgeInset.width * 2 > NSMaxX(_screenRect))))) { if (contentRect.size.width >= _maxHeight) { _maxHeight = contentRect.size.width; } else { contentRect.size.width = _maxHeight; - _view.textView.textContainer.containerSize = NSMakeSize(_maxHeight, maxTextHeight); + _view.textView.textContainer.size = NSMakeSize(_maxHeight, maxTextHeight); } } // the sweep direction of the client app changes the behavior of adjusting squirrel panel position NSRect windowRect; bool sweepVertical = NSWidth(_position) > NSHeight(_position); - NSSize scaledRectSize = NSMakeSize(contentRect.size.width + theme.edgeInset.width * 2, - contentRect.size.height + theme.edgeInset.height * 2); if (theme.vertical) { - windowRect.size = NSMakeSize(scaledRectSize.height / theme.scaleFactor, scaledRectSize.width / theme.scaleFactor); + windowRect.size = NSMakeSize(ceil(contentRect.size.height + theme.edgeInset.height * 2), + ceil(contentRect.size.width + theme.edgeInset.width * 2)); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width / theme.scaleFactor : -kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } // Make the right edge of candidate block fixed at the left of cursor windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { - CGFloat preeditHeight = NSHeight([_view contentRectForRange:_view.preeditRange]); - windowRect.origin.x += (preeditHeight + theme.edgeInset.height) / theme.scaleFactor; + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.x += ceil(NSHeight(preeditRect) + theme.edgeInset.height); } } else { - windowRect.size = NSMakeSize(scaledRectSize.width / theme.scaleFactor, scaledRectSize.height / theme.scaleFactor); + windowRect.size = NSMakeSize(ceil(contentRect.size.width + theme.edgeInset.width * 2), + ceil(contentRect.size.height + theme.edgeInset.height * 2)); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight + theme.edgeInset.width / theme.scaleFactor; + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width / theme.scaleFactor, + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } @@ -1122,18 +1132,15 @@ - (void)show { // rotate the view, the core in vertical mode! if (theme.vertical) { self.contentView.boundsRotation = -90.0; - [self.contentView setBoundsSize:NSMakeSize(scaledRectSize.height, scaledRectSize.width)]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, windowRect.size.width)]; } else { self.contentView.boundsRotation = 0.0; - [self.contentView setBoundsSize:NSMakeSize(scaledRectSize.width, scaledRectSize.height)]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; } - - [_view setFrame:self.contentView.bounds]; - [_view.textView setFrame:self.contentView.bounds]; _view.textView.boundsRotation = 0.0; [_view.textView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; + [_view setFrame:self.contentView.bounds]; + [_view.textView setFrame:NSInsetRect(self.contentView.bounds, theme.edgeInset.width, theme.edgeInset.height)]; BOOL translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency) { @@ -1239,7 +1246,7 @@ - (void)showPreedit:(NSString *)preedit attributes:theme.preeditAttrs]]; } - fixDefaultFont(preeditLine); + fixDefaultFont(preeditLine, theme.vertical); [preeditLine addAttribute:NSParagraphStyleAttributeName value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; @@ -1248,32 +1255,30 @@ - (void)showPreedit:(NSString *)preedit preeditRange = NSMakeRange(0, text.length); preeditWidth = NSWidth([preeditLine boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); if (numCandidates) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" - attributes:theme.preeditAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } } // prepare paging and separator for width calculation but no insertion yet - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" - attributes:theme.pagingAttrs]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"◀\ufe0e" : @"◁\ufe0e").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:theme.linear ? @" %lu " : @"\t%lu\t", pageNum+1] + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%lu", pageNum+1] attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"▷\ufe0e" : @"▶\ufe0e").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs]]; - fixDefaultFont(paging); - CGFloat pagingWidth = theme.showPaging ? NSMaxX([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; - - CGFloat separatorWidth = theme.linear ? NSMaxX([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"\u25C0\uFE0E" : @"\u25C1\uFE0E").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs] atIndex:0]; // ◀ and ◁ + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"\u25B7\uFE0E" : @"\u25B6\uFE0E").precomposedStringWithCanonicalMapping + attributes:theme.pagingAttrs]]; // ▷ and ▶ + + fixDefaultFont(paging, theme.vertical); + CGFloat pagingWidth = theme.showPaging ? NSWidth([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + CGFloat separatorWidth = theme.linear ? NSWidth([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; CGFloat lineWidth = 0.0 - separatorWidth; CGFloat maxLineWidth = MIN(preeditWidth, _maxTextWidth); - BOOL multiLine = NO; + BOOL useTab = NO; // candidates NSUInteger candidateBlockStart = text.length; NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; - for (NSUInteger i = 0; i < candidates.count; ++i) { + for (NSUInteger i = 0; i < candidates.count; i += 1) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; @@ -1296,8 +1301,7 @@ - (void)showPreedit:(NSString *)preedit prefixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString - attributes:labelAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString attributes:labelAttrs]]; // get the label size for indent if (!theme.linear) { labelWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); @@ -1333,11 +1337,10 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString - attributes:attrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString attributes:attrs]]; } - fixDefaultFont(line); + fixDefaultFont(line, theme.vertical); NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; // determine if the line is too wide and line break is needed, based on screen size. NSString *separtatorString = @"\u2029"; @@ -1348,17 +1351,20 @@ - (void)showPreedit:(NSString *)preedit } if (lineWidth + separatorWidth + candidateWidth > _maxTextWidth) { separtatorString = @"\u2028"; - multiLine = YES; - maxLineWidth = MAX(maxLineWidth, lineWidth); + maxLineWidth = MAX(maxLineWidth, MIN(lineWidth, _maxTextWidth)); lineWidth = candidateWidth; } else { separtatorString = @" "; lineWidth += separatorWidth + candidateWidth; } - } else { + } else { // stacked candidates maxLineWidth = MAX(maxLineWidth, MIN(candidateWidth, _maxTextWidth)); paragraphStyleCandidate.headIndent = labelWidth; } + if (i == numCandidates-1 && theme.showPaging) { + maxLineWidth = MAX(maxLineWidth, _maxHeight); + useTab = !((theme.linear ? lineWidth : pagingWidth) >= maxLineWidth); + } NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; if (i > 0) { @@ -1374,11 +1380,8 @@ - (void)showPreedit:(NSString *)preedit // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates && theme.showPaging) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (multiLine ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (useTab ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; pagingRange = NSMakeRange(text.length, paging.length); - if (theme.rememberSize) { - maxLineWidth = MAX(maxLineWidth, _maxHeight); - } if (theme.linear) { [text appendAttributedString:paging]; NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; @@ -1386,8 +1389,12 @@ - (void)showPreedit:(NSString *)preedit [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], - [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; + if (useTab) { + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:paging.length-2]; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:2]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], + [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; + } [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; } @@ -1429,7 +1436,7 @@ - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; [text addAttribute:NSKernAttributeName value:@(0) range:NSMakeRange(0, text.length)]; - fixDefaultFont(text); + fixDefaultFont(text, theme.vertical); [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; @@ -1520,12 +1527,12 @@ static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, } } --(void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { +- (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; [[self class] updateTheme:theme withConfig:config forDarkMode:isDark]; } -+(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { ++ (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { BOOL linear = NO; BOOL vertical = NO; updateCandidateListLayout(&linear, config, @"style"); @@ -1544,7 +1551,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; - CGFloat alpha = fmin(fmax([config getDouble:@"style/alpha"], 0.0), 1.0); + CGFloat alpha = MIN(MAX([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; @@ -1552,6 +1559,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; + CGFloat kerning = [config getDouble:@"style/kerning"]; NSColor *backgroundColor; NSColor *backgroundImage; @@ -1582,7 +1590,7 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]]; } backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]]; - backgroundImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]]; + backgroundImage = [config getPattern:[prefix stringByAppendingString:@"/back_image"]]; borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]]; preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]]; textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]]; @@ -1591,29 +1599,23 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (highlightedTextColor == nil) { highlightedTextColor = textColor; } - highlightedBackColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]]; - candidateTextColor = - [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]]; + highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]]; + candidateTextColor = [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]]; if (candidateTextColor == nil) { // in non-inline mode, 'text_color' is for rendering preedit text. // if not otherwise specified, candidate text is also rendered in this color. candidateTextColor = textColor; } - highlightedCandidateTextColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; + highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; if (highlightedCandidateTextColor == nil) { highlightedCandidateTextColor = highlightedTextColor; } - highlightedCandidateBackColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_back_color"]]; + highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_back_color"]]; if (highlightedCandidateBackColor == nil) { highlightedCandidateBackColor = highlightedBackColor; } - commentTextColor = - [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; - highlightedCommentTextColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_comment_text_color"]]; + commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; + highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_comment_text_color"]]; // the following per-color-scheme configurations, if exist, will // override configurations with the same name under the global 'style' section @@ -1621,118 +1623,99 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo updateCandidateListLayout(&linear, config, prefix); updateTextOrientation(&vertical, config, prefix); - NSNumber *inlinePreeditOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]]; + NSNumber *inlinePreeditOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]]; if (inlinePreeditOverridden) { inlinePreedit = inlinePreeditOverridden.boolValue; } - NSNumber *inlineCandidateOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]]; + NSNumber *inlineCandidateOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]]; if (inlineCandidateOverridden) { inlineCandidate = inlineCandidateOverridden.boolValue; } - NSNumber *translucencyOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/translucency"]]; + NSNumber *translucencyOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/translucency"]]; if (translucencyOverridden) { translucency = translucencyOverridden.boolValue; } - NSNumber *showPagingOverridden = - [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]]; + NSNumber *showPagingOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]]; if (showPagingOverridden) { showPaging = showPagingOverridden.boolValue; } - NSString *candidateFormatOverridden = - [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; + NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; if (candidateFormatOverridden) { candidateFormat = candidateFormatOverridden; } - NSString *fontNameOverridden = - [config getString:[prefix stringByAppendingString:@"/font_face"]]; + NSString *fontNameOverridden = [config getString:[prefix stringByAppendingString:@"/font_face"]]; if (fontNameOverridden) { fontName = fontNameOverridden; } - NSNumber *fontSizeOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; + NSNumber *fontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; if (fontSizeOverridden) { fontSize = fontSizeOverridden.doubleValue; } - NSString *labelFontNameOverridden = - [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; + NSString *labelFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; if (labelFontNameOverridden) { labelFontName = labelFontNameOverridden; } - NSNumber *labelFontSizeOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; + NSNumber *labelFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; if (labelFontSizeOverridden) { labelFontSize = labelFontSizeOverridden.doubleValue; } - NSString *commentFontNameOverridden = - [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; + NSString *commentFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; if (commentFontNameOverridden) { commentFontName = commentFontNameOverridden; } - NSNumber *commentFontSizeOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; + NSNumber *commentFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; if (commentFontSizeOverridden) { commentFontSize = commentFontSizeOverridden.doubleValue; } - NSColor *candidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_color"]]; + NSColor *candidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_color"]]; if (candidateLabelColorOverridden) { candidateLabelColor = candidateLabelColorOverridden; } - NSColor *highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; + NSColor *highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; if (!highlightedCandidateLabelColorOverridden) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' - // are both valid - highlightedCandidateLabelColorOverridden = - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid + highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; } if (highlightedCandidateLabelColorOverridden) { highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; } - NSNumber *alphaOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; + NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { alpha = fmin(fmax(alphaOverridden.doubleValue, 0.0), 1.0); } - NSNumber *cornerRadiusOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]]; + NSNumber *cornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]]; if (cornerRadiusOverridden) { cornerRadius = cornerRadiusOverridden.doubleValue; } - NSNumber *hilitedCornerRadiusOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]]; + NSNumber *hilitedCornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]]; if (hilitedCornerRadiusOverridden) { hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; } - NSNumber *borderHeightOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; + NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { borderHeight = borderHeightOverridden.doubleValue; } - NSNumber *borderWidthOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]]; + NSNumber *borderWidthOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]]; if (borderWidthOverridden) { borderWidth = borderWidthOverridden.doubleValue; } - NSNumber *lineSpacingOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]]; + NSNumber *lineSpacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]]; if (lineSpacingOverridden) { lineSpacing = lineSpacingOverridden.doubleValue; } - NSNumber *spacingOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]]; + NSNumber *spacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]]; if (spacingOverridden) { spacing = spacingOverridden.doubleValue; } - NSNumber *baseOffsetOverridden = - [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]]; + NSNumber *baseOffsetOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]]; if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } + NSNumber *kerningOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/kerning"]]; + if (kerningOverridden) { + kerning = kerningOverridden.doubleValue; + } } if (fontSize == 0) { // default size @@ -1744,28 +1727,27 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo if (commentFontSize == 0) { commentFontSize = fontSize; } - CGFloat scaleFactor = MAX(1.0, kDefaultFontSize/fontSize); NSFontDescriptor *fontDescriptor = nil; NSFont *font = nil; if (fontName != nil) { fontDescriptor = getFontDescriptor(fontName); if (fontDescriptor != nil) { - font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize * scaleFactor]; + font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize]; } } if (font == nil) { // use default font - font = [NSFont userFontOfSize:fontSize * scaleFactor]; + font = [NSFont userFontOfSize:fontSize]; } NSFontDescriptor *labelFontDescriptor = nil; NSFont *labelFont = nil; if (labelFontName != nil) { labelFontDescriptor = getFontDescriptor(labelFontName); if (labelFontDescriptor != nil) { - labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize * scaleFactor]; + labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; } } if (labelFont == nil) { - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize * scaleFactor weight:NSFontWeightRegular]; + labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; } NSFontDescriptor *commentFontDescriptor = nil; NSFont *commentFont = nil; @@ -1775,32 +1757,32 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo commentFontDescriptor = fontDescriptor; } if (commentFontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:commentFontDescriptor size:commentFontSize * scaleFactor]; + commentFont = [NSFont fontWithDescriptor:commentFontDescriptor size:commentFontSize]; } } if (commentFont == nil) { if (fontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:fontDescriptor size:commentFontSize * scaleFactor]; + commentFont = [NSFont fontWithDescriptor:fontDescriptor size:commentFontSize]; } else { - commentFont = [NSFont fontWithName:font.fontName size:commentFontSize * scaleFactor]; + commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; } } - NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize * scaleFactor]; + NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize]; - CGFloat fontLineHeight = MAX(font.ascender - font.descender, [NSFont systemFontOfSize:fontSize].ascender - [NSFont systemFontOfSize:fontSize].descender); - CGFloat commentLineHeight = MAX(commentFont.ascender - commentFont.descender, [NSFont systemFontOfSize:commentFontSize].ascender - [NSFont systemFontOfSize:commentFontSize].descender); - CGFloat labelLineHeight = MAX(labelFont.ascender - labelFont.descender, [NSFont systemFontOfSize:labelFontSize].ascender - [NSFont systemFontOfSize:labelFontSize].descender); - CGFloat lineHeight = MAX(fontLineHeight, MAX(commentLineHeight, labelLineHeight)); + CGFloat fontHeight = font.ascender - font.descender; + CGFloat commentFontHeight = commentFont.ascender - commentFont.descender; + CGFloat labelFontHeight = labelFont.ascender - labelFont.descender; + CGFloat lineHeight = MAX(fontHeight, MAX(commentFontHeight, labelFontHeight)); NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.alignment = NSTextAlignmentLeft; - preeditParagraphStyle.minimumLineHeight = fontLineHeight; - preeditParagraphStyle.paragraphSpacing = spacing/2.0 * scaleFactor; + preeditParagraphStyle.minimumLineHeight = fontHeight; + preeditParagraphStyle.paragraphSpacing = spacing / 2; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; paragraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing/2.0 * scaleFactor; - paragraphStyle.lineSpacing = lineSpacing/2.0 * scaleFactor; + paragraphStyle.minimumLineHeight = lineHeight + lineSpacing / 2; + paragraphStyle.lineSpacing = lineSpacing / 2; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; pagingParagraphStyle.alignment = NSTextAlignmentLeft; @@ -1830,18 +1812,26 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; pagingAttrs[NSFontAttributeName] = pagingFont; - attrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset * scaleFactor - ceil(labelFontSize/5.0)); + attrs[NSBaselineOffsetAttributeName] = @(baseOffset); + highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + attrs[NSKernAttributeName] = @(kerning); + highlightedAttrs[NSKernAttributeName] = @(kerning); + labelAttrs[NSKernAttributeName] = @(kerning); + labelHighlightedAttrs[NSKernAttributeName] = @(kerning); + commentAttrs[NSKernAttributeName] = @(kerning); + commentHighlightedAttrs[NSKernAttributeName] = @(kerning); + preeditAttrs[NSKernAttributeName] = @(kerning); + preeditHighlightedAttrs[NSKernAttributeName] = @(kerning); + pagingAttrs[NSKernAttributeName] = @(kerning); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; - backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; borderColor = borderColor ? borderColor : [NSColor gridColor]; preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : [NSColor windowBackgroundColor]; @@ -1901,17 +1891,16 @@ +(void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config fo preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; - borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)) * scaleFactor; + borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)); + borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)); NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:cornerRadius * scaleFactor - hilitedCornerRadius:hilitedCornerRadius * scaleFactor + [theme setCornerRadius:cornerRadius + hilitedCornerRadius:hilitedCornerRadius edgeInset:edgeInset borderWidth:MAX(borderHeight, borderWidth) - linespace:lineSpacing * scaleFactor - preeditLinespace:spacing * scaleFactor - scaleFactor:scaleFactor + linespace:lineSpacing + preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency showPaging:showPaging From 132698a6208118a4c70947396c889a20cff026b6 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 9 May 2023 14:53:58 +0200 Subject: [PATCH 071/164] typesetting --- Squirrel.xcodeproj/project.pbxproj | 4 +- data/squirrel.yaml | 100 +++++++++++++++-------------- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 423eebde5..c40b28c66 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -752,7 +752,7 @@ /usr/local/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; @@ -805,7 +805,7 @@ /usr/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = 10.9; + MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 52e850ea7..9ac7d90d4 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -39,6 +39,8 @@ style: line_spacing: 5 # space between preedit and candidates in non-inline mode spacing: 10 + # space betweeen letters and characters, a.k.a. tracking + kerning: 1 #candidate_format: '%c. %@' @@ -181,16 +183,16 @@ preset_color_schemes: google: name: 谷歌/Google author: skoj - text_color: 0x666666 #拼音串 - candidate_text_color: 0x000000 #非第一候选项 - back_color: 0xFFFFFF #背景 - border_color: 0xE2E2E2 #边框 - hilited_text_color: 0x000000 #拼音串高亮 - hilited_back_color: 0xFFFFFF #拼音串高亮背景 - hilited_candidate_text_color: 0xFFFFFF #第一候选项 - hilited_candidate_back_color: 0xCE7539 #第一候选项背景 - comment_text_color: 0x6D6D6D #注解文字 - hilited_comment_text_color: 0xEBC6B0 #注解文字高亮 + text_color: 0x666666 # 拼音串 + candidate_text_color: 0x000000 # 非第一候选项 + back_color: 0xFFFFFF # 背景 + border_color: 0xE2E2E2 # 边框 + hilited_text_color: 0x000000 # 拼音串高亮 + hilited_back_color: 0xFFFFFF # 拼音串高亮背景 + hilited_candidate_text_color: 0xFFFFFF # 第一候选项 + hilited_candidate_back_color: 0xCE7539 # 第一候选项背景 + comment_text_color: 0x6D6D6D # 注解文字 + hilited_comment_text_color: 0xEBC6B0 # 注解文字高亮 solarized_rock: name: 曬經石/Solarized Rock @@ -228,57 +230,57 @@ preset_color_schemes: apathy: name: 冷漠/Apathy author: LIANG Hai - horizontal: true # 水平排列 - inline_preedit: true #单行显示,false双行显示 - candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间 - corner_radius: 5 #候选条圆角 + horizontal: true # 水平排列 + inline_preedit: true # 单行显示,false双行显示 + candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间 + corner_radius: 5 # 候选条圆角 border_height: 0 border_width: 0 - back_color: 0xFFFFFF #候选条背景色 - font_face: "PingFangSC-Regular,HanaMinB" #候选词字体 - font_point: 16 #候选字词大小 - text_color: 0x424242 #高亮选中词颜色 - label_font_face: "STHeitiSC-Light" #候选词编号字体 - label_font_point: 12 #候选编号大小 - hilited_candidate_text_color: 0xEE6E00 #候选文字颜色 - hilited_candidate_back_color: 0xFFF0E4 #候选文字背景色 - comment_text_color: 0x999999 #拼音等提示文字颜色 + back_color: 0xFFFFFF # 候选条背景色 + font_face: "PingFangSC-Regular,HanaMinB" # 候选词字体 + font_point: 16 # 候选字词大小 + text_color: 0x424242 # 高亮选中词颜色 + label_font_face: "STHeitiSC-Light" # 候选词编号字体 + label_font_point: 12 # 候选编号大小 + hilited_candidate_text_color: 0xEE6E00 # 候选文字颜色 + hilited_candidate_back_color: 0xFFF0E4 # 候选文字背景色 + comment_text_color: 0x999999 # 拼音等提示文字颜色 dust: name: 浮尘/Dust author: Superoutman - horizontal: true # 水平排列 - inline_preedit: true #单行显示,false双行显示 - candidate_format: "%c\u2005%@\u2005" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 - corner_radius: 2 #候选条圆角 - border_height: 3 # 窗口边界高度,大于圆角半径才生效 - border_width: 8 # 窗口边界宽度,大于圆角半径才生效 - back_color: 0xeeffffff #候选条背景色 - border_color: 0xE0B693 # 边框色 - font_face: "HYQiHei-55S Book,HanaMinA Regular" #候选词字体 - font_point: 14 #候选字词大小 - label_font_face: "SimHei" #候选词编号字体 - label_font_point: 10 #候选编号大小 - label_color: 0xcbcbcb # 预选栏编号颜色 - candidate_text_color: 0x555555 # 预选项文字颜色 - text_color: 0x424242 # 拼音行文字颜色,24位色值,16进制,BGR顺序 - comment_text_color: 0x999999 # 拼音等提示文字颜色 - hilited_text_color: 0x9e9e9e # 高亮拼音 (需要开启内嵌编码) - hilited_candidate_text_color: 0x000000 # 第一候选项文字颜色 - hilited_candidate_back_color: 0xfff0e4 # 第一候选项背景背景色 - hilited_candidate_label_color: 0x555555 # 第一候选项编号颜色 - hilited_comment_text_color: 0x9e9e9e # 注解文字高亮 + horizontal: true # 水平排列 + inline_preedit: true # 单行显示,false双行显示 + candidate_format: "%c\u2005%@\u2005" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 + corner_radius: 2 # 候选条圆角 + border_height: 3 # 窗口边界高度,大于圆角半径×(1-1/√2)才生效 + border_width: 8 # 窗口边界宽度,大于圆角半径×(1-1/√2)才生效 + back_color: 0xeeffffff # 候选条背景色 + border_color: 0xE0B693 # 边框色 + font_face: "HYQiHei-55S Book,HanaMinA Regular" # 候选词字体 + font_point: 14 # 候选字词大小 + label_font_face: "SimHei" # 候选词编号字体 + label_font_point: 10 # 候选编号大小 + label_color: 0xcbcbcb # 预选栏编号颜色 + candidate_text_color: 0x555555 # 预选项文字颜色 + text_color: 0x424242 # 拼音行文字颜色,24位色值,16进制,BGR顺序 + comment_text_color: 0x999999 # 拼音等提示文字颜色 + hilited_text_color: 0x9e9e9e # 高亮拼音 (需要开启内嵌编码) + hilited_candidate_text_color: 0x000000 # 第一候选项文字颜色 + hilited_candidate_back_color: 0xfff0e4 # 第一候选项背景背景色 + hilited_candidate_label_color: 0x555555 # 第一候选项编号颜色 + hilited_comment_text_color: 0x9e9e9e # 注解文字高亮 mojave_dark: name: 沙漠夜/Mojave Dark author: xiehuc horizontal: true # 水平排列 inline_preedit: true # 单行显示,false双行显示 - candidate_format: "%c\u2005%@" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 + candidate_format: "%c\u2005%@" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 corner_radius: 5 # 候选条圆角 hilited_corner_radius: 3 # 高亮圆角 - border_height: 6 # 窗口边界高度,大于圆角半径才生效 - border_width: 6 # 窗口边界宽度,大于圆角半径才生效 + border_height: 6 # 窗口边界高度,大于圆角半径×(1-1/√2)才生效 + border_width: 6 # 窗口边界宽度,大于圆角半径×(1-1/√2)才生效 font_face: "PingFangSC" # 候选词字体 font_point: 16 # 候选字词大小 label_font_point: 14 # 候选编号大小 @@ -294,12 +296,12 @@ preset_color_schemes: hilited_candidate_back_color: 0xcb5d00 # 第一候选项背景背景色 hilited_candidate_label_color: 0xffffff # 第一候选项编号颜色 comment_text_color: 0xdedddd # 拼音等提示文字颜色 - #hilited_comment_text_color: 0xdedddd # 注解文字高亮 + #hilited_comment_text_color: 0xdedddd # 注解文字高亮 solarized_light: name: 曬經・日/Solarized Light author: 雪齋 - color_space: display_p3 # Only available on macOS 10.12+ + color_space: display_p3 # Only available on macOS 10.12+ back_color: 0xF0E5F6FB #Lab 97 , 0 , 10 border_color: 0xEDFFFF #Lab 100, 0 , 10 preedit_back_color: 0x403516 #Lab 20 ,-12,-12 From 8af12044fb43ca6880df1db9647c26388ba63e0a Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 12 May 2023 05:27:37 +0200 Subject: [PATCH 072/164] clean up --- .../RimeIcon.appiconset/rime-16 1.png | Bin 2075 -> 0 bytes .../RimeIcon.appiconset/rime-32 1.png | Bin 2352 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16 1.png delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-32 1.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16 1.png b/Assets.xcassets/RimeIcon.appiconset/rime-16 1.png deleted file mode 100644 index 119930f708412156be3bdab913742a914925febd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2075 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 From 1b5dcaa59960c40b91b01ac1fccc97491169a993 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 12 May 2023 08:32:41 +0200 Subject: [PATCH 073/164] Update SquirrelPanel.m --- SquirrelPanel.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 9e766de17..5872de1a7 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1381,7 +1381,7 @@ - (void)showPreedit:(NSString *)preedit NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates && theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (useTab ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; - pagingRange = NSMakeRange(text.length, paging.length); + NSUInteger pagingStart = text.length; if (theme.linear) { [text appendAttributedString:paging]; NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; @@ -1398,6 +1398,7 @@ - (void)showPreedit:(NSString *)preedit [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; } + pagingRange = NSMakeRange(pagingStart, paging.length); } // extra line fragment will not actually be drawn but ensures the spacing after the last line @@ -1820,7 +1821,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.2)); attrs[NSKernAttributeName] = @(kerning); highlightedAttrs[NSKernAttributeName] = @(kerning); labelAttrs[NSKernAttributeName] = @(kerning); From cc476afdc7f1673854fd288183681c1169f9a310 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 13 May 2023 00:28:39 +0200 Subject: [PATCH 074/164] Update --- .gitmodules | 1 + INSTALL.md | 2 +- Sparkle | 2 +- Squirrel.xcodeproj/project.pbxproj | 2 +- librime | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index cc39e6359..1ebd84b73 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,3 +9,4 @@ [submodule "Sparkle"] path = Sparkle url = https://github.com/sparkle-project/Sparkle + branch = 2.x diff --git a/INSTALL.md b/INSTALL.md index a2c55e83d..462a6ee79 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua lotem/librime-proto # rime/librime-charcode rime/librime-legacy ... ``` ### Shortcut: get the latest librime release diff --git a/Sparkle b/Sparkle index 8fb9c83ad..4a3b1e679 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 +Subproject commit 4a3b1e6793ec1bb41139dd721ba76de642840c3d diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index c40b28c66..bebd9c005 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -806,7 +806,7 @@ ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; - ONLY_ACTIVE_ARCH = YES; + ONLY_ACTIVE_ARCH = NO; SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; }; diff --git a/librime b/librime index c5564f9b7..75f109893 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit c5564f9b7a9e9a2c7a6d0eb3abc38d12b9568e92 +Subproject commit 75f1098933664c26c03c61f4be9fa3504bab808a From 94641212c691e8bbdc19c49a69d1c50a0c0f2f24 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 13 May 2023 00:28:02 +0200 Subject: [PATCH 075/164] choose candidate (selection without committance) --- SquirrelInputController.h | 6 ++- SquirrelInputController.m | 47 ++++++++++++-------- SquirrelPanel.m | 93 +++++++++++++++++++++------------------ librime | 2 +- 4 files changed, 84 insertions(+), 64 deletions(-) diff --git a/SquirrelInputController.h b/SquirrelInputController.h index da3aa21a5..424973b44 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -2,5 +2,9 @@ #import @interface SquirrelInputController : IMKInputController -- (BOOL)actionWithCandidate:(NSInteger)index; +- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index; @end + +#define kSELECT 0x1 +#define kDELETE 0x2 +#define kCHOOSE 0x3 diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 2253ca1fc..3357035fe 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -20,6 +20,7 @@ -(void)updateAppOptions; @implementation SquirrelInputController { id _currentClient; NSString *_preeditString; + NSString *_inlineString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; @@ -206,18 +207,22 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers return handled; } -- (BOOL)actionWithCandidate:(NSInteger)index { +- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index { BOOL handled = NO; - if (index == NSPageUpFunctionKey) { + if (index == NSPageUpFunctionKey && action == kSELECT) { handled = rime_get_api()->process_key(_session, XK_Page_Up, 0); - } else if (index == NSPageDownFunctionKey) { + } else if (index == NSPageDownFunctionKey && action == kSELECT) { handled = rime_get_api()->process_key(_session, XK_Page_Down, 0); } else if (index >= 0 && index < 10) { - handled = rime_get_api()->select_candidate_on_current_page(_session, (int)index); - } else if (index >= -10 && index <= -1) { // -1-index for deletion - handled = rime_get_api()->delete_candidate_on_current_page(_session, (int)-1-index); + if (action == kSELECT) { + handled = rime_get_api()->select_candidate_on_current_page(_session, (int)index); + } else if (action == kCHOOSE) { + handled = rime_get_api()->choose_candidate_on_current_page(_session, (int)index); + } else if (action == kDELETE) { + handled = rime_get_api()->delete_candidate_on_current_page(_session, (int)index); + } } - if (handled) { + if (handled && action != kCHOOSE) { [self rimeUpdate]; } return handled; @@ -410,11 +415,11 @@ -(void)showPreeditString:(NSString*)preedit { //NSLog(@"showPreeditString: '%@'", preedit); - if ([_preeditString isEqualToString:preedit] && + if ([_inlineString isEqualToString:preedit] && _caretPos == pos && _selRange.location == range.location && _selRange.length == range.length) return; - _preeditString = preedit; + _inlineString = preedit; _selRange = range; _caretPos = pos; @@ -447,6 +452,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage + update:(BOOL)update { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; @@ -465,7 +471,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit pageNum:pageNum lastPage:lastPage turnPage:NSNotFound - update:YES]; + update:update]; } @end // SquirrelController @@ -547,7 +553,7 @@ -(void)rimeUpdate _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !status.is_disabled); } rime_get_api()->free_status(&status); } @@ -557,6 +563,8 @@ -(void)rimeUpdate // update preedit text const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; + BOOL update = [_preeditString isEqualToString:preeditText] ? NSNotFound : YES; + if (update) _preeditString = preeditText; NSUInteger start = substr(preedit, ctx.composition.sel_start).length; NSUInteger end = substr(preedit, ctx.composition.sel_end).length; @@ -566,21 +574,21 @@ -(void)rimeUpdate const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit) { - if ((caretPos >= NSMaxRange(selRange)) && (caretPos < preeditText.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length-caretPos)]]; + if ((caretPos >= NSMaxRange(selRange)) && (caretPos < _preeditString.length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString:[_preeditString substringWithRange:NSMakeRange(caretPos, _preeditString.length-caretPos)]]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(preeditText.length-caretPos)]; + [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(_preeditString.length-caretPos)]; } else { if ((NSMaxRange(selRange) < caretPos) && (caretPos > selRange.location)) { candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(caretPos-NSMaxRange(selRange)))]; - } else if ((NSMaxRange(selRange) < preeditText.length) && (caretPos <= selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(preeditText.length-NSMaxRange(selRange)))]; + } else if ((NSMaxRange(selRange) < _preeditString.length) && (caretPos <= selRange.location)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(_preeditString.length-NSMaxRange(selRange)))]; } [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; } } else { if (_inlinePreedit) { - [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; + [self showPreeditString:_preeditString selRange:selRange caretPos:caretPos]; } else { NSRange empty = {0, 0}; // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. @@ -615,7 +623,7 @@ -(void)rimeUpdate } else { labels = @[]; } - [self showPanelWithPreedit:(_inlinePreedit ? nil : preeditText) + [self showPanelWithPreedit:(_inlinePreedit ? nil : _preeditString) selRange:selRange caretPos:caretPos candidates:candidates @@ -623,7 +631,8 @@ -(void)rimeUpdate labels:labels highlighted:ctx.menu.highlighted_candidate_index pageNum:ctx.menu.page_no - lastPage:ctx.menu.is_last_page]; + lastPage:ctx.menu.is_last_page + update:update]; rime_get_api()->free_context(&ctx); } else { [NSApp.squirrelAppDelegate.panel hide]; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 5872de1a7..fcb339e27 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -255,6 +255,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) NSRect preeditBlockRect; @property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingRects; @property(nonatomic, readonly) NSUInteger pagingButton; @@ -570,6 +571,7 @@ - (void)drawRect:(NSRect)dirtyRect { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; } } + _preeditBlockRect = preeditRect; // Draw paging Rect NSRect pagingRect = NSZeroRect; @@ -762,18 +764,18 @@ - (void)drawRect:(NSRect)dirtyRect { } } -- (BOOL)clickAtPoint:(NSPoint)_point index:(NSInteger *)_index { - if (CGPathContainsPoint(_shape.path, NULL, _point, NO)) { - if (_pagingRects[0] != nil && NSPointInRect(_point, _pagingRects[0].rectValue)) { - *_index = NSPageUpFunctionKey; +- (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { + if (NSPointInRect(spot, self.bounds)) { + if (_pagingRects[0] != nil && NSPointInRect(spot, _pagingRects[0].rectValue)) { + *index = NSPageUpFunctionKey; return YES; - } else if (_pagingRects[1] != nil && NSPointInRect(_point, _pagingRects[1].rectValue)) { - *_index = NSPageDownFunctionKey; + } else if (_pagingRects[1] != nil && NSPointInRect(spot, _pagingRects[1].rectValue)) { + *index = NSPageDownFunctionKey; return YES; } for (NSUInteger i = 0; i < _candidatePaths.count; i++) { - if ([_candidatePaths[i] containsPoint:_point]) { - *_index = i; + if ([_candidatePaths[i] containsPoint:spot]) { + *index = i; return YES; } } @@ -801,7 +803,7 @@ @implementation SquirrelPanel { NSUInteger _pageNum; NSInteger _turnPage; BOOL _lastPage; - NSUInteger _cursorIndex; + BOOL _mouseDown; NSString *_statusMessage; NSTimer *_statusTimer; @@ -970,34 +972,35 @@ - (NSPoint)mousePosition { } - (void)sendEvent:(NSEvent *)event { + BOOL handled = NO; + NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSUInteger cursorIndex = NSNotFound; switch (event.type) { case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: { - NSPoint point = [self mousePosition]; - NSInteger index = NSNotFound; - if ([_view clickAtPoint:point index:&index]) { - if ((index >= 0 && index < _candidates.count) || - index == NSPageUpFunctionKey || index == NSPageDownFunctionKey) { - _index = index; + if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { + if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || + cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) { + _index = cursorIndex; + _mouseDown = YES; + handled = YES; } } } break; case NSEventTypeLeftMouseUp: { - NSPoint point = [self mousePosition]; - NSInteger index = NSNotFound; - if ([_view clickAtPoint:point index:&index]) { - if (((index >= 0 && index < _candidates.count) || index == NSPageUpFunctionKey || - index == NSPageDownFunctionKey) && index == _index) { - [_inputController actionWithCandidate:index]; + if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index && ((cursorIndex >= 0 && cursorIndex < _candidates.count) || + cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey)) { + handled = [_inputController perform:kSELECT onIndex:cursorIndex]; + _mouseDown = NO; } } } break; case NSEventTypeRightMouseUp: { - NSPoint point = [self mousePosition]; - NSInteger index = NSNotFound; - if ([_view clickAtPoint:point index:&index]) { - if ((index >= 0 && index < _candidates.count) && index == _index) { - [_inputController actionWithCandidate:-1-index]; // negative index for deletion + if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { + handled = [_inputController perform:kDELETE onIndex:cursorIndex]; + _mouseDown = NO; } } } break; @@ -1006,29 +1009,30 @@ - (void)sendEvent:(NSEvent *)event { } break; case NSEventTypeMouseExited: { self.acceptsMouseMovedEvents = NO; - if (_cursorIndex != _index) { - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; - } } break; case NSEventTypeMouseMoved: { - NSPoint point = [self mousePosition]; - NSInteger index = NSNotFound; - if ([_view clickAtPoint: point index:&index]) { - if (index >= 0 && index < _candidates.count && _cursorIndex != index) { - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:index pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; - } else if (index == NSPageUpFunctionKey || index == NSPageDownFunctionKey || - index == NSBeginFunctionKey || index == NSEndFunctionKey) { // borrow corresponding unicodes for readability - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels - highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:index update:NO]; + if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { + [_inputController perform:kCHOOSE onIndex:cursorIndex]; + _index = cursorIndex; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + handled = YES; + } else if (cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey || + cursorIndex == NSBeginFunctionKey || cursorIndex == NSEndFunctionKey) { // borrow corresponding unicodes for readability + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + handled = YES; } } } break; + case NSEventTypeLeftMouseDragged: { + _mouseDown = NO; + [self performWindowDragWithEvent:event]; + handled = YES; + } break; default: break; } - [super sendEvent:event]; + if (!handled) [super sendEvent:event]; } - (void)getCurrentScreen { @@ -1178,6 +1182,10 @@ - (void)showPreedit:(NSString *)preedit lastPage:(BOOL)lastPage turnPage:(NSUInteger)turnPage update:(BOOL)update { + if (update == NSNotFound && _index != index && + _pageNum == pageNum && _lastPage == lastPage) { + update = NO; + } if (update) { _preedit = preedit; _selRange = selRange; @@ -1193,7 +1201,6 @@ - (void)showPreedit:(NSString *)preedit if (numCandidates == 0) { _index = index = NSNotFound; } - _cursorIndex = index; if (turnPage == NSPageUpFunctionKey || turnPage == NSBeginFunctionKey) { _turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; @@ -1411,7 +1418,7 @@ - (void)showPreedit:(NSString *)preedit // text done! [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:_turnPage]; - [self show]; + if (update) [self show]; } - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { diff --git a/librime b/librime index 75f109893..2c142e295 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 75f1098933664c26c03c61f4be9fa3504bab808a +Subproject commit 2c142e295dbbff710a1756efb11ab0c0bc90056a From 8e04890cd223eb8e9c5811aa9fdc704b7a1a1de8 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 14 May 2023 21:10:40 +0200 Subject: [PATCH 076/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 2c142e295..f1c8aac13 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 2c142e295dbbff710a1756efb11ab0c0bc90056a +Subproject commit f1c8aac13aeaddeeefe6b1ba015cdadeb2dcc1ee From c666c2d65723d4d928ed3075f007225b05fed818 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 15 May 2023 12:13:04 +0200 Subject: [PATCH 077/164] Update SquirrelInputController.m --- SquirrelInputController.m | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 3357035fe..e80717831 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -92,24 +92,25 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender [self processKey:rime_keycode modifiers:rime_modifiers]; } if (changes & OSX_SHIFT_MASK) { - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_CTRL_MASK) { - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_ALT_MASK) { - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_FN_MASK) { - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } if (changes & OSX_COMMAND_MASK) { - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + _lastEventCount = eventCount; // do not update UI when using Command key break; } From b0e43f9d3f4c919cbe316c77ddcfb1dc46bfc963 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 15 May 2023 18:11:28 +0200 Subject: [PATCH 078/164] version control --- .gitmodules | 2 +- INSTALL.md | 2 +- Sparkle | 2 +- librime | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 1ebd84b73..26ab0c14c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ [submodule "Sparkle"] path = Sparkle url = https://github.com/sparkle-project/Sparkle - branch = 2.x + branch = 2.4.1 diff --git a/INSTALL.md b/INSTALL.md index 462a6ee79..4b14ed8f8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/Sparkle b/Sparkle index 4a3b1e679..76c6522b2 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 4a3b1e6793ec1bb41139dd721ba76de642840c3d +Subproject commit 76c6522b235d612bca2ed5d2c57b1c302e6b0902 diff --git a/librime b/librime index f1c8aac13..b1fd36842 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit f1c8aac13aeaddeeefe6b1ba015cdadeb2dcc1ee +Subproject commit b1fd36842c788d64d1f7e636c66df5ba4d595c79 From 29e4747a5750a1d4ead87ee3f6d0ab3930980c37 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 18 May 2023 18:59:18 +0200 Subject: [PATCH 079/164] scroll --- SquirrelPanel.m | 61 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index fcb339e27..e1994ab39 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -255,7 +255,6 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; -@property(nonatomic, readonly) NSRect preeditBlockRect; @property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingRects; @property(nonatomic, readonly) NSUInteger pagingButton; @@ -306,6 +305,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { if (self) { self.wantsLayer = YES; self.layer.masksToBounds = YES; + self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; } _textView = [[NSTextView alloc] initWithFrame:frameRect]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; @@ -571,7 +571,6 @@ - (void)drawRect:(NSRect)dirtyRect { preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; } } - _preeditBlockRect = preeditRect; // Draw paging Rect NSRect pagingRect = NSZeroRect; @@ -804,6 +803,7 @@ @implementation SquirrelPanel { NSInteger _turnPage; BOOL _lastPage; BOOL _mouseDown; + NSPoint _scrollLocus; NSString *_statusMessage; NSTimer *_statusTimer; @@ -965,19 +965,13 @@ - (instancetype)init { return self; } -- (NSPoint)mousePosition { - NSPoint point = NSEvent.mouseLocation; - point = [self convertPointFromScreen:point]; - return [_view convertPoint:point fromView:nil]; -} - - (void)sendEvent:(NSEvent *)event { BOOL handled = NO; - NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; - NSUInteger cursorIndex = NSNotFound; switch (event.type) { case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: { + NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) { @@ -988,18 +982,22 @@ - (void)sendEvent:(NSEvent *)event { } } break; case NSEventTypeLeftMouseUp: { + NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index && ((cursorIndex >= 0 && cursorIndex < _candidates.count) || cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey)) { - handled = [_inputController perform:kSELECT onIndex:cursorIndex]; + handled = [self.inputController perform:kSELECT onIndex:cursorIndex]; _mouseDown = NO; } } } break; case NSEventTypeRightMouseUp: { + NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { - handled = [_inputController perform:kDELETE onIndex:cursorIndex]; + handled = [self.inputController perform:kDELETE onIndex:cursorIndex]; _mouseDown = NO; } } @@ -1011,9 +1009,11 @@ - (void)sendEvent:(NSEvent *)event { self.acceptsMouseMovedEvents = NO; } break; case NSEventTypeMouseMoved: { + NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { - [_inputController perform:kCHOOSE onIndex:cursorIndex]; + [self.inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; handled = YES; @@ -1029,6 +1029,35 @@ - (void)sendEvent:(NSEvent *)event { [self performWindowDragWithEvent:event]; handled = YES; } break; + case NSEventTypeScrollWheel: { + CGFloat scrollThreshold = [_view.currentTheme.attrs[NSParagraphStyleAttributeName] minimumLineHeight]; + if (event.phase == NSEventPhaseBegan) { + _scrollLocus = NSZeroPoint; + handled = YES; + } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && + _scrollLocus.x != NSNotFound && _scrollLocus.y != NSNotFound) { + // determine scrolling direction by confining to sectors within ±30º of any axis + if (ABS(event.scrollingDeltaX) > ABS(event.scrollingDeltaY) * sqrt(3)) { + _scrollLocus.x += event.scrollingDeltaX * (event.hasPreciseScrollingDeltas ? 1 : 10); + } else if (ABS(event.scrollingDeltaY) > ABS(event.scrollingDeltaX) * sqrt(3)) { + _scrollLocus.y += event.scrollingDeltaY * (event.hasPreciseScrollingDeltas ? 1 : 10); + } + // compare accumulated locus length against threshold and limit paging to max once + if (_scrollLocus.x > scrollThreshold) { + handled = [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + } else if (_scrollLocus.y > scrollThreshold) { + handled = [self.inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; + _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + } else if (_scrollLocus.x < -scrollThreshold) { + handled = [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + } else if (_scrollLocus.y < -scrollThreshold) { + handled = [self.inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; + _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + } + } + } break; default: break; } @@ -1145,6 +1174,7 @@ - (void)show { [_view.textView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; [_view setFrame:self.contentView.bounds]; [_view.textView setFrame:NSInsetRect(self.contentView.bounds, theme.edgeInset.width, theme.edgeInset.height)]; + BOOL translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency) { @@ -1182,9 +1212,8 @@ - (void)showPreedit:(NSString *)preedit lastPage:(BOOL)lastPage turnPage:(NSUInteger)turnPage update:(BOOL)update { - if (update == NSNotFound && _index != index && - _pageNum == pageNum && _lastPage == lastPage) { - update = NO; + if (update == NSNotFound) { + update = _index != index && _pageNum == pageNum && _lastPage == lastPage ? NO : YES; } if (update) { _preedit = preedit; From de3d1f5eeb3a5c570f0ab22f0ebbd1fe4a8b1d55 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 16 May 2023 08:07:37 +0200 Subject: [PATCH 080/164] Update SquirrelInputController.m --- SquirrelInputController.m | 87 +++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index e80717831..fd7439432 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -25,7 +25,6 @@ @implementation SquirrelInputController { NSUInteger _caretPos; NSArray *_candidates; NSUInteger _lastModifier; - NSEventType _lastEventType; uint32_t _lastEventCount; RimeSessionId _session; NSString *_schemaId; @@ -53,9 +52,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender // Returning NO means the original key down will be passed on to the client. _currentClient = sender; - - CGEventFlags modifiers = CGEventGetFlags(event.CGEvent); - + NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; + uint32_t eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType); BOOL handled = NO; @autoreleasepool { @@ -79,43 +77,50 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender handled = YES; break; } + //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; - NSUInteger changes = _lastModifier ^ modifiers; int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - int64_t keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); - int rime_keycode = osx_keycode_to_rime_keycode((int)keyCode, 0, 0, 0); - _lastModifier = modifiers; - uint32_t eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType); - if (changes & OSX_CAPITAL_MASK) { - rime_modifiers ^= kLockMask; - [self processKey:rime_keycode modifiers:rime_modifiers]; - } - if (changes & OSX_SHIFT_MASK) { - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } - if (changes & OSX_CTRL_MASK) { - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } - if (changes & OSX_ALT_MASK) { - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } - if (changes & OSX_FN_MASK) { - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } - if (changes & OSX_COMMAND_MASK) { - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - _lastEventCount = eventCount; - // do not update UI when using Command key - break; + CGKeyCode keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); + int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); + + switch (keyCode) { + case kVK_CapsLock: { + rime_modifiers ^= kLockMask; + [self processKey:rime_keycode modifiers:rime_modifiers]; + } break; + case kVK_Shift: + case kVK_RightShift: { + release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + } break; + case kVK_Control: + case kVK_RightControl: { + release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + } break; + case kVK_Option: + case kVK_RightOption: { + release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + } break; + case kVK_Function: { + release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + } break; + case kVK_Command: + case kVK_RightCommand: { + release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + goto saveStatus; + } + default: + break; } [self rimeUpdate]; + saveStatus: _lastEventCount = eventCount; + _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -123,11 +128,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; int keyCode = event.keyCode; - NSString* keyChars = event.charactersIgnoringModifiers; - if ((modifiers & OSX_SHIFT_MASK) && - !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) { - keyChars = event.characters; - } + NSString *keyChars = ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && + !(modifiers & OSX_ALT_MASK)) ? event.characters : event.charactersIgnoringModifiers; //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); @@ -149,9 +151,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } } - - _lastEventType = event.type; - return handled; } @@ -554,7 +553,7 @@ -(void)rimeUpdate _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !status.is_disabled); + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); } rime_get_api()->free_status(&status); } From 1965f01ac8140eed03cd23b4b9ffaf64f6ac63d8 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 18 May 2023 18:59:29 +0200 Subject: [PATCH 081/164] prevent autorelease --- SquirrelInputController.m | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index fd7439432..5c58036cf 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -91,26 +91,26 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } break; case kVK_Shift: case kVK_RightShift: { - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } break; case kVK_Control: case kVK_RightControl: { - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } break; case kVK_Option: case kVK_RightOption: { - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } break; case kVK_Function: { - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; } break; case kVK_Command: case kVK_RightCommand: { - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount <= 1 ? 0 : kIgnoredMask); + release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; goto saveStatus; } @@ -118,9 +118,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; - saveStatus: - _lastEventCount = eventCount; - _lastModifier = modifiers; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -151,6 +148,11 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } } + + saveStatus: if (event.type == NSEventTypeFlagsChanged) { + _lastModifier = modifiers; + _lastEventCount = eventCount; + } return handled; } From 8de872daee17d55e6fb65861950ad2058cc3ab7b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 19 May 2023 01:23:50 +0200 Subject: [PATCH 082/164] Update INSTALL.md --- INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.md b/INSTALL.md index 4b14ed8f8..822f4753a 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua lotem/librime-proto # rime/librime-charcode rime/librime-legacy ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... ``` ### Shortcut: get the latest librime release From f4aea55e5fe01eac238d60eb1abf93b5ba44b586 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 19 May 2023 03:06:59 +0200 Subject: [PATCH 083/164] allow mouse move between modifier down and up --- SquirrelInputController.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 5c58036cf..ff73f6fef 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -53,7 +53,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender _currentClient = sender; NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; - uint32_t eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGAnyInputEventType); BOOL handled = NO; @autoreleasepool { @@ -83,7 +82,12 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); CGKeyCode keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); - + int eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); + _lastModifier = modifiers; switch (keyCode) { case kVK_CapsLock: { rime_modifiers ^= kLockMask; @@ -118,6 +122,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } [self rimeUpdate]; + saveStatus: _lastEventCount = eventCount; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -148,11 +153,6 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender break; } } - - saveStatus: if (event.type == NSEventTypeFlagsChanged) { - _lastModifier = modifiers; - _lastEventCount = eventCount; - } return handled; } From 209f4f203fb905351369bee2cb3df2babcef942d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 19 May 2023 03:06:59 +0200 Subject: [PATCH 084/164] allow mouse move between modifier down and up --- SquirrelInputController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index ff73f6fef..cc50cec8e 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -25,7 +25,7 @@ @implementation SquirrelInputController { NSUInteger _caretPos; NSArray *_candidates; NSUInteger _lastModifier; - uint32_t _lastEventCount; + int _lastEventCount; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; From 10b834383c7e4a78e9f281bcfac4261af7bbfbb1 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 20 May 2023 00:11:30 +0200 Subject: [PATCH 085/164] Robust scenarios in remember_size --- SquirrelPanel.m | 92 +++++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index e1994ab39..748d12029 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -761,6 +761,7 @@ - (void)drawRect:(NSRect)dirtyRect { borderLayer.strokeColor = [theme.borderColor CGColor]; [panelLayer addSublayer:borderLayer]; } + [_textView setTextContainerInset:theme.edgeInset]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { @@ -789,7 +790,7 @@ @implementation SquirrelPanel { NSVisualEffectView *_back; NSRect _screenRect; - CGFloat _maxHeight; + NSSize _maxSize; CGFloat _maxTextWidth; NSString *_preedit; @@ -960,7 +961,7 @@ - (instancetype)init { if (@available(macOS 10.14, *)) { [self initializeUIStyleForDarkMode:YES]; } - _maxHeight = 0; + _maxSize = NSZeroSize; } return self; } @@ -1101,24 +1102,36 @@ - (void)show { } _view.textView.textContainer.size = NSMakeSize(textWidth, maxTextHeight); - // in vertical mode, the width and height are interchanged + bool sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; - if (theme.rememberSize && (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : - ((NSMinX(_position) + MAX(contentRect.size.width, _maxHeight) + theme.edgeInset.width * 2 > NSMaxX(_screenRect))))) { - if (contentRect.size.width >= _maxHeight) { - _maxHeight = contentRect.size.width; - } else { - contentRect.size.width = _maxHeight; - _view.textView.textContainer.size = NSMakeSize(_maxHeight, maxTextHeight); + NSRect maxContentRect = contentRect; + // remember panel size (fix the top leading anchor of the panel in screen coordiantes) + if (theme.rememberSize) { + if (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : + (sweepVertical ? (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) : + (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.edgeInset.width > NSMaxX(_screenRect)))) { + if (NSWidth(maxContentRect) >= _maxSize.width) { + _maxSize.width = NSWidth(maxContentRect); + } else { + maxContentRect.size.width = _maxSize.width; + _view.textView.textContainer.size = NSMakeSize(_maxSize.width, maxTextHeight); + } + } + if (theme.vertical ? (NSMinX(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : + (NSMinY(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? 0 : kOffsetHeight))) { + if (NSHeight(maxContentRect) >= _maxSize.height) { + _maxSize.height = NSHeight(maxContentRect); + } else { + maxContentRect.size.height = _maxSize.height; + } } } // the sweep direction of the client app changes the behavior of adjusting squirrel panel position NSRect windowRect; - bool sweepVertical = NSWidth(_position) > NSHeight(_position); if (theme.vertical) { - windowRect.size = NSMakeSize(ceil(contentRect.size.height + theme.edgeInset.height * 2), - ceil(contentRect.size.width + theme.edgeInset.width * 2)); + windowRect.size = NSMakeSize(NSHeight(maxContentRect) + theme.edgeInset.height * 2, + NSWidth(maxContentRect) + theme.edgeInset.width * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width : -kOffsetHeight) - NSHeight(windowRect); @@ -1129,11 +1142,11 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += ceil(NSHeight(preeditRect) + theme.edgeInset.height); + windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height; } } else { - windowRect.size = NSMakeSize(ceil(contentRect.size.width + theme.edgeInset.width * 2), - ceil(contentRect.size.height + theme.edgeInset.height * 2)); + windowRect.size = NSMakeSize(NSWidth(maxContentRect) + theme.edgeInset.width * 2, + NSHeight(maxContentRect) + theme.edgeInset.height * 2); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { @@ -1161,31 +1174,38 @@ - (void)show { windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); } - [self setFrame:windowRect display:YES]; + if (theme.vertical) { + windowRect.origin.x += NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.size.width -= NSHeight(maxContentRect) - NSHeight(contentRect); + } else { + windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); + } + [self setFrame:NSIntegralRectWithOptions(windowRect, NSAlignAllEdgesOutward) display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { - self.contentView.boundsRotation = -90.0; - [self.contentView setBoundsOrigin:NSMakePoint(0.0, windowRect.size.width)]; + [self.contentView setBoundsRotation:-90.0]; + [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { - self.contentView.boundsRotation = 0.0; + [self.contentView setBoundsRotation:0.0]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; } - _view.textView.boundsRotation = 0.0; - [_view.textView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; + [_view.textView setBoundsRotation:0.0]; + [_view.textView setBoundsOrigin:_view.textView.textContainerOrigin]; [_view setFrame:self.contentView.bounds]; - [_view.textView setFrame:NSInsetRect(self.contentView.bounds, theme.edgeInset.width, theme.edgeInset.height)]; + [_view.textView setFrame:self.contentView.bounds]; BOOL translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency) { [_back setFrame:self.contentView.bounds]; - _back.appearance = NSApp.effectiveAppearance; + [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; } else { [_back setHidden:YES]; } } - self.alphaValue = theme.alpha; + [self setAlphaValue:theme.alpha]; [self invalidateShadow]; [self orderFront:nil]; // voila ! @@ -1197,7 +1217,7 @@ - (void)hide { _statusTimer = nil; } [self orderOut:nil]; - _maxHeight = 0; + _maxSize = NSZeroSize; } // Main function to add attributes to text output from librime @@ -1231,12 +1251,18 @@ - (void)showPreedit:(NSString *)preedit _index = index = NSNotFound; } - if (turnPage == NSPageUpFunctionKey || turnPage == NSBeginFunctionKey) { - _turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } else if (turnPage == NSPageDownFunctionKey || turnPage == NSEndFunctionKey) { - _turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; - } else { - _turnPage = NSNotFound; + switch(turnPage) { + case NSPageUpFunctionKey: + case NSBeginFunctionKey: { + _turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; + } break; + case NSPageDownFunctionKey: + case NSEndFunctionKey: { + _turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + } break; + default: { + _turnPage = NSNotFound; + } break; } if (numCandidates || (preedit && preedit.length)) { @@ -1398,7 +1424,7 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate.headIndent = labelWidth; } if (i == numCandidates-1 && theme.showPaging) { - maxLineWidth = MAX(maxLineWidth, _maxHeight); + maxLineWidth = MAX(maxLineWidth, _maxSize.width); useTab = !((theme.linear ? lineWidth : pagingWidth) >= maxLineWidth); } NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; From 99b8e189686079422bb5ecd5168f0b1b55dd3f4e Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 20 May 2023 10:43:48 +0200 Subject: [PATCH 086/164] update libs --- INSTALL.md | 2 +- Sparkle | 2 +- librime | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 822f4753a..a2c55e83d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/Sparkle b/Sparkle index 76c6522b2..7907f058b 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 76c6522b235d612bca2ed5d2c57b1c302e6b0902 +Subproject commit 7907f058bcef1132c9b4af6c049cac598330a5f9 diff --git a/librime b/librime index b1fd36842..7558d7bbf 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit b1fd36842c788d64d1f7e636c66df5ba4d595c79 +Subproject commit 7558d7bbf717758e338984af2ead8e4c8be3e6b3 From c3d4fad4f8013794f05a90605e341e81f5400893 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 25 May 2023 17:06:44 +0200 Subject: [PATCH 087/164] advance calculation of maxTextWidth --- SquirrelPanel.m | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 748d12029..b3369a908 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1080,7 +1080,6 @@ - (void)getCurrentScreen { // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { - [self getCurrentScreen]; SquirrelTheme *theme = _view.currentTheme; if (@available(macOS 10.14, *)) { @@ -1092,10 +1091,7 @@ - (void)show { //Break line if the text is too long, based on screen size. CGFloat textWidth = _view.textView.textStorage.size.width; - NSFont *currentFont = theme.attrs[NSFontAttributeName]; - CGFloat fontScale = currentFont.pointSize / 12.0; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + fontScale / 12.0); - _maxTextWidth = theme.vertical ? (NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2) : (NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2); + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); CGFloat maxTextHeight = theme.vertical ? (NSWidth(_screenRect) - theme.edgeInset.width * 2) : (NSHeight(_screenRect) - theme.edgeInset.height * 2); if (textWidth > _maxTextWidth) { textWidth = _maxTextWidth; @@ -1282,6 +1278,9 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; + [self getCurrentScreen]; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + _maxTextWidth = theme.vertical ? (NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2) : (NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2); NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); From 60294894a6aa1358d4ea248716021c69a769274d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 25 May 2023 18:40:48 +0200 Subject: [PATCH 088/164] allow no color --- SquirrelPanel.m | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index b3369a908..56e66098b 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -709,12 +709,6 @@ - (void)drawRect:(NSRect)dirtyRect { [self.layer addSublayer:panelLayer]; if (theme.preeditBackgroundColor && (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { - if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { - CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; - highlightedLayer.path = [highlightedPath quartzPath]; - highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; - [panelLayer addSublayer:highlightedLayer]; - } CAShapeLayer *nonCandidateLayer = [[CAShapeLayer alloc] init]; nonCandidateBlockPath = [backgroundPath copy]; if (![candidateBlockPath isEmpty]) { @@ -726,6 +720,12 @@ - (void)drawRect:(NSRect)dirtyRect { nonCandidateLayer.fillColor = [theme.preeditBackgroundColor CGColor]; [panelLayer addSublayer:nonCandidateLayer]; } + if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { + CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; + highlightedLayer.path = [highlightedPath quartzPath]; + highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; + [panelLayer addSublayer:highlightedLayer]; + } if (theme.highlightedPreeditColor) { if (_pagingRange.length > 0) { CAShapeLayer *pageUpLayer = [[CAShapeLayer alloc] init]; @@ -1656,8 +1656,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]]; preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]]; textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]]; - highlightedTextColor = - [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]]; + highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]]; if (highlightedTextColor == nil) { highlightedTextColor = textColor; } @@ -1895,11 +1894,11 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSColor *secondaryTextColor = [[self class] secondaryTextColor]; backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; - borderColor = borderColor ? borderColor : [NSColor gridColor]; - preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : [NSColor windowBackgroundColor]; + borderColor = borderColor ? borderColor : isNative ? [NSColor gridColor] : nil; + preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : isNative ? [NSColor windowBackgroundColor] : nil; candidateTextColor = candidateTextColor ? candidateTextColor : [NSColor controlTextColor]; highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedMenuItemTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : [NSColor selectedContentBackgroundColor]; + highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : isNative ? [NSColor selectedContentBackgroundColor] : nil; candidateLabelColor = candidateLabelColor ? candidateLabelColor : isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : @@ -1908,7 +1907,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : [NSColor alternateSelectedControlTextColor]; textColor = textColor ? textColor : [NSColor textColor]; highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor selectedTextColor]; - highlightedBackColor = highlightedBackColor ? highlightedBackColor : [NSColor selectedTextBackgroundColor]; + highlightedBackColor = highlightedBackColor ? highlightedBackColor : isNative ? [NSColor selectedTextBackgroundColor] : nil; attrs[NSForegroundColorAttributeName] = candidateTextColor; labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; From 442593fcd9dcd59e10d45ec5bbf72d752563492d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 26 May 2023 21:55:51 +0200 Subject: [PATCH 089/164] disable remember_size for status messages --- SquirrelPanel.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 56e66098b..20cd6a4db 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1505,6 +1505,7 @@ - (void)showStatus:(NSString *)message { _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSRange emptyRange = NSMakeRange(NSNotFound, 0); + _maxSize = NSZeroSize; // disable remember_size for status messages [_view drawViewWith:@[] highlightedIndex:NSNotFound preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange pagingButton:NSNotFound]; [self show]; From 221ddf8be03ea76f38f71a2a128dba8cdc2791d1 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 26 May 2023 21:58:11 +0200 Subject: [PATCH 090/164] Update libs --- Sparkle | 2 +- librime | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sparkle b/Sparkle index 7907f058b..ec547530e 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 7907f058bcef1132c9b4af6c049cac598330a5f9 +Subproject commit ec547530e9e50181574a1eae7dc9c021800415d8 diff --git a/librime b/librime index 7558d7bbf..32958f7b5 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 7558d7bbf717758e338984af2ead8e4c8be3e6b3 +Subproject commit 32958f7b5cbaa3d4ed61e7deb410c1995b3ab39b From ea91592f8d05c7fd85fb7854b11fb8a0d8c54563 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 30 May 2023 15:09:44 +0200 Subject: [PATCH 091/164] no caret with swtichers --- SquirrelInputController.m | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index cc50cec8e..204ecf47d 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -541,6 +541,7 @@ -(void)rimeUpdate { //NSLog(@"rimeUpdate"); [self rimeConsumeCommittedText]; + BOOL switcher = rime_get_api()->get_option(_session, "dumb"); RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { @@ -555,7 +556,7 @@ -(void)rimeUpdate _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !switcher); } rime_get_api()->free_status(&status); } @@ -575,7 +576,7 @@ -(void)rimeUpdate if (_inlineCandidate) { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; - if (_inlinePreedit) { + if (_inlinePreedit && !switcher) { if ((caretPos >= NSMaxRange(selRange)) && (caretPos < _preeditString.length)) { candidatePreviewText = [candidatePreviewText stringByAppendingString:[_preeditString substringWithRange:NSMakeRange(caretPos, _preeditString.length-caretPos)]]; } @@ -589,7 +590,7 @@ -(void)rimeUpdate [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; } } else { - if (_inlinePreedit) { + if (_inlinePreedit && !switcher) { [self showPreeditString:_preeditString selRange:selRange caretPos:caretPos]; } else { NSRange empty = {0, 0}; @@ -625,7 +626,7 @@ -(void)rimeUpdate } else { labels = @[]; } - [self showPanelWithPreedit:(_inlinePreedit ? nil : _preeditString) + [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : _preeditString) selRange:selRange caretPos:caretPos candidates:candidates From 741a889e519db8a38fe869825088dbc50f684dbd Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 30 May 2023 16:36:08 +0200 Subject: [PATCH 092/164] backward compatibility backward compatibility & fix line height --- SquirrelApplicationDelegate.m | 14 +- SquirrelInputController.m | 28 +- SquirrelPanel.m | 985 ++++++++++++++++++---------------- data/squirrel.yaml | 12 +- 4 files changed, 551 insertions(+), 488 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index db1479ab6..81cbe9ffc 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -185,13 +185,23 @@ -(BOOL)problematicLaunchDetected options:NSDataReadingUncached error:nil]; if (archive) { - NSDate* previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; + NSDate* previousLaunch; + if (@available(macOS 10.13, *)) { + previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; + } else { + previousLaunch = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; + } if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate* now = [NSDate date]; - NSData* record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; + NSData* record; + if (@available(macOS 10.13, *)) { + record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; + } else { + record = [NSKeyedArchiver archivedDataWithRootObject:now]; + } [record writeToFile:logfile atomically:NO]; return detected; } diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 204ecf47d..13940adb5 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -417,11 +417,11 @@ -(void)showPreeditString:(NSString*)preedit { //NSLog(@"showPreeditString: '%@'", preedit); - if ([_inlineString isEqualToString:preedit] && + if ([_preeditString isEqualToString:preedit] && _caretPos == pos && _selRange.location == range.location && _selRange.length == range.length) return; - _inlineString = preedit; + _preeditString = preedit; _selRange = range; _caretPos = pos; @@ -454,7 +454,6 @@ -(void)showPanelWithPreedit:(NSString*)preedit highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage - update:(BOOL)update { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; @@ -473,7 +472,7 @@ -(void)showPanelWithPreedit:(NSString*)preedit pageNum:pageNum lastPage:lastPage turnPage:NSNotFound - update:update]; + update:YES]; } @end // SquirrelController @@ -534,7 +533,7 @@ -(void)rimeConsumeCommittedText char substring[length+1]; strncpy(substring, str, length); substring[length] = '\0'; - return [NSString stringWithUTF8String:substring]; + return @(substring); } -(void)rimeUpdate @@ -566,8 +565,6 @@ -(void)rimeUpdate // update preedit text const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; - BOOL update = [_preeditString isEqualToString:preeditText] ? NSNotFound : YES; - if (update) _preeditString = preeditText; NSUInteger start = substr(preedit, ctx.composition.sel_start).length; NSUInteger end = substr(preedit, ctx.composition.sel_end).length; @@ -577,21 +574,21 @@ -(void)rimeUpdate const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit && !switcher) { - if ((caretPos >= NSMaxRange(selRange)) && (caretPos < _preeditString.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[_preeditString substringWithRange:NSMakeRange(caretPos, _preeditString.length-caretPos)]]; + if ((caretPos >= NSMaxRange(selRange)) && (caretPos < preeditText.length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length-caretPos)]]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(_preeditString.length-caretPos)]; + [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(preeditText.length-caretPos)]; } else { if ((NSMaxRange(selRange) < caretPos) && (caretPos > selRange.location)) { candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(caretPos-NSMaxRange(selRange)))]; - } else if ((NSMaxRange(selRange) < _preeditString.length) && (caretPos <= selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(_preeditString.length-NSMaxRange(selRange)))]; + } else if ((NSMaxRange(selRange) < preeditText.length) && (caretPos <= selRange.location)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(preeditText.length-NSMaxRange(selRange)))]; } [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; } } else { if (_inlinePreedit && !switcher) { - [self showPreeditString:_preeditString selRange:selRange caretPos:caretPos]; + [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; } else { NSRange empty = {0, 0}; // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. @@ -626,7 +623,7 @@ -(void)rimeUpdate } else { labels = @[]; } - [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : _preeditString) + [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) selRange:selRange caretPos:caretPos candidates:candidates @@ -634,8 +631,7 @@ -(void)rimeUpdate labels:labels highlighted:ctx.menu.highlighted_candidate_index pageNum:ctx.menu.page_no - lastPage:ctx.menu.is_last_page - update:update]; + lastPage:ctx.menu.is_last_page]; rime_get_api()->free_context(&ctx); } else { [NSApp.squirrelAppDelegate.panel hide]; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 20cd6a4db..f6cb50a6c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -9,7 +9,7 @@ - (CGPathRef)quartzPath { // Need to begin a path here. CGPathRef immutablePath = NULL; // Then draw the path elements. - NSUInteger numElements = [self elementCount]; + NSUInteger numElements = self.elementCount; if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; @@ -39,7 +39,7 @@ - (CGPathRef)quartzPath { if (!didClosePath) { CGPathCloseSubpath(path); } - immutablePath = CGPathCreateCopy(path); + immutablePath = CFAutorelease(CGPathCreateCopy(path)); CGPathRelease(path); } return immutablePath; @@ -50,7 +50,7 @@ - (CGPathRef)quartzPath { static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kDefaultCandidateFormat = @"%c %@"; @interface SquirrelTheme : NSObject @@ -66,11 +66,10 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; @property(nonatomic, readonly) NSSize edgeInset; -@property(nonatomic, readonly) CGFloat borderWidth; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; -@property(nonatomic, readonly) BOOL translucency; +@property(nonatomic, readonly) CGFloat translucency; @property(nonatomic, readonly) BOOL showPaging; @property(nonatomic, readonly) BOOL rememberSize; @property(nonatomic, readonly) BOOL linear; @@ -87,6 +86,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSDictionary *preeditAttrs; @property(nonatomic, strong, readonly) NSDictionary *preeditHighlightedAttrs; @property(nonatomic, strong, readonly) NSDictionary *pagingAttrs; +@property(nonatomic, strong, readonly) NSDictionary *pagingHighlightedAttrs; @property(nonatomic, strong, readonly) NSParagraphStyle *paragraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; @@ -108,11 +108,10 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius edgeInset:(NSSize)edgeInset - borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha - translucency:(BOOL)translucency + translucency:(CGFloat)translucency showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize linear:(BOOL)linear @@ -121,14 +120,15 @@ - (void)setCornerRadius:(CGFloat)cornerRadius inlineCandidate:(BOOL)inlineCandidate; - (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs; + pagingAttrs:(NSMutableDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs; - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle @@ -189,11 +189,10 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius edgeInset:(NSSize)edgeInset - borderWidth:(CGFloat)borderWidth linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha - translucency:(BOOL)translucency + translucency:(CGFloat)translucency showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize linear:(BOOL)linear @@ -203,13 +202,12 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; _edgeInset = edgeInset; - _borderWidth = borderWidth; _linespace = linespace; + _preeditLinespace = preeditLinespace; _alpha = alpha; _translucency = translucency; _showPaging = showPaging; _rememberSize = rememberSize; - _preeditLinespace = preeditLinespace; _linear = linear; _vertical = vertical; _inlinePreedit = inlinePreedit; @@ -217,23 +215,25 @@ - (void)setCornerRadius:(CGFloat)cornerRadius } - (void) setAttrs:(NSMutableDictionary *)attrs - labelAttrs:(NSMutableDictionary *)labelAttrs highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs commentAttrs:(NSMutableDictionary *)commentAttrs commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs preeditAttrs:(NSMutableDictionary *)preeditAttrs preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs { + pagingAttrs:(NSMutableDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs{ _attrs = attrs; - _labelAttrs = labelAttrs; _highlightedAttrs = highlightedAttrs; + _labelAttrs = labelAttrs; _labelHighlightedAttrs = labelHighlightedAttrs; _commentAttrs = commentAttrs; _commentHighlightedAttrs = commentHighlightedAttrs; _preeditAttrs = preeditAttrs; _preeditHighlightedAttrs = preeditHighlightedAttrs; _pagingAttrs = pagingAttrs; + _pagingHighlightedAttrs = pagingHighlightedAttrs; } - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -250,27 +250,32 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSTextView *textView; @property(nonatomic, readonly) NSArray *candidateRanges; -@property(nonatomic, readonly) NSInteger highlightedIndex; +@property(nonatomic, readonly) NSUInteger highlightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSRect contentRect; @property(nonatomic, readonly) NSMutableArray *candidatePaths; -@property(nonatomic, readonly) NSMutableArray *pagingRects; +@property(nonatomic, readonly) NSMutableArray *pagingPaths; @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; +@property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; - (BOOL)isFlipped; @property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; - (void) drawViewWith:(NSArray *)candidateRanges - highlightedIndex:(NSInteger)highlightedIndex + highlightedIndex:(NSUInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; +- (NSRect)setLineRectForRange:(NSRange)charRange + atOrigin:(NSPoint)origin + withReferenceFont:(NSFont *)refFont + paragraphStyle:(NSParagraphStyle *)style; @end @implementation SquirrelView @@ -309,17 +314,15 @@ - (instancetype)initWithFrame:(NSRect)frameRect { } _textView = [[NSTextView alloc] initWithFrame:frameRect]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; - textContainer.lineFragmentPadding = 0.0; _textView.drawsBackground = NO; _textView.editable = NO; _textView.selectable = NO; + _textView.wantsLayer = NO; [_textView replaceTextContainer:textContainer]; _textView.layoutManager.backgroundLayoutEnabled = YES; _textView.layoutManager.usesFontLeading = NO; - _textView.layoutManager.typesetterBehavior = NSTypesetterBehavior_10_4; + _textView.layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; _defaultTheme = [[SquirrelTheme alloc] init]; - _candidatePaths = [NSMutableArray arrayWithCapacity:_candidateRanges.count]; - _pagingRects = [NSMutableArray arrayWithCapacity:2]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; @@ -327,26 +330,108 @@ - (instancetype)initWithFrame:(NSRect)frameRect { return self; } +- (NSRect)setLineRectForRange:(NSRange)charRange + atOrigin:(NSPoint)origin + withReferenceFont:(NSFont *)refFont + paragraphStyle:(NSParagraphStyle *)style { + NSLayoutManager *layoutManager = self.textView.layoutManager; + NSTextContainer *textContainer = self.textView.textContainer; + NSTextStorage *textStorage = self.textView.textStorage; + + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + NSRect blockRect = NSMakeRect(origin.x, origin.y, 0, 0); + BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + CGFloat refFontHeight = [layoutManager defaultLineHeightForFont:refFont]; + CGFloat refBaseline = [layoutManager defaultBaselineOffsetForFont:refFont]; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + + NSUInteger i = glyphRange.location; + NSRange lineRange = NSMakeRange(i, 0); + while (i < NSMaxRange(glyphRange)) { + // typsetting the line fragment + NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; + NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; + NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; + rect.origin.y = NSMaxY(blockRect); + usedRect.origin.y = NSMaxY(blockRect); + CGFloat alignment = verticalLayout ? lineHeight/2 : (refBaseline + lineHeight/2 - refFontHeight/2); + rect.size.height = lineHeight; + usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); + if (style.lineSpacing > 0) { + rect.size.height = lineHeight + style.lineSpacing; + usedRect.size.height = MAX(NSHeight(usedRect), lineHeight + style.lineSpacing); + alignment += style.lineSpacing/2; + } + if (style.paragraphSpacing > 0 && [textStorage.string characterAtIndex:NSMaxRange(lineCharRange)-1] == '\n') { + rect.size.height += style.paragraphSpacing; + } + if (style.paragraphSpacingBefore > 0 && (lineCharRange.location == 0 || + [textStorage.string characterAtIndex:lineCharRange.location-1] == '\n')) { + rect.size.height += style.paragraphSpacingBefore; + usedRect.origin.y += style.paragraphSpacingBefore; + alignment += style.paragraphSpacingBefore; + } + [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:NSIntersectionRect(usedRect, rect)]; + + // typesetting glyphs + NSRange fontRunRange = NSMakeRange(NSNotFound, 0); + NSUInteger j = lineRange.location; + while (j < NSMaxRange(lineRange)) { + NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; + NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; + NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; + NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; + NSRange runRange = NSIntersectionRange(fontRunRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + if (verticalLayout) { + runFont = runFont.verticalFont; + resizedRefFont = resizedRefFont.verticalFont; + } + CGFloat runBaseline = [layoutManager defaultBaselineOffsetForFont:runFont]; + CGFloat runFontHeight = [layoutManager defaultLineHeightForFont:runFont]; + CGFloat resizedRefFontHeight = [layoutManager defaultLineHeightForFont:resizedRefFont]; + CGFloat resizedRefBaseline = [layoutManager defaultBaselineOffsetForFont:resizedRefFont]; + CGFloat runFontOvershoot = MAX(0.0, runFontHeight - runBaseline - resizedRefFontHeight + resizedRefBaseline)/2; + runGlyphPosition.y = alignment + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + if (verticalLayout) { + if (runFont.verticalFont.isVertical) { + runGlyphPosition.x += runFontOvershoot; + } else { + runGlyphPosition.y += runBaseline - runFontHeight/2; + } + } + [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; + j = NSMaxRange(runRange); + } + blockRect = NSUnionRect(blockRect, rect); + i = NSMaxRange(lineRange); + } + return blockRect; +} + // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; - NSRange glyphRange = [_textView.layoutManager glyphRangeForTextContainer:_textView.textContainer]; - NSRect rect = [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; - NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL withoutAdditionalLayout:YES]; - rect.size.height = NSMaxY(finalLineRect) - NSMinY(rect); + [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; + NSRect rect = [self.textView.layoutManager usedRectForTextContainer:self.textView.textContainer]; + NSRect extraLineRect = [self.textView.layoutManager extraLineFragmentRect]; + rect.size.height -= NSHeight(extraLineRect); return rect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { - NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; - return rect; + NSSize edgeInset = self.currentTheme.edgeInset; + NSRange glyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [self.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textView.textContainer]; + NSRect firstLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:NULL]; + NSRect finalLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; + return NSMakeRect(NSMinX(rect) + edgeInset.width, NSMinY(firstLineRect) + edgeInset.height, + NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); } // Will triger - (void)drawRect:(NSRect)dirtyRect - (void) drawViewWith:(NSArray*)candidateRanges - highlightedIndex:(NSInteger)highlightedIndex + highlightedIndex:(NSUInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange @@ -357,6 +442,8 @@ - (void) drawViewWith:(NSArray*)candidateRanges _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; _pagingButton = pagingButton; + _candidatePaths = [NSMutableArray arrayWithCapacity:candidateRanges.count]; + _pagingPaths = [NSMutableArray arrayWithCapacity:pagingRange.length > 0 ? 2 : 0]; self.needsDisplay = YES; } @@ -389,7 +476,7 @@ - (void) drawViewWith:(NSArray*)candidateRanges target.y += sign(diff.dy/beta)*beta; } [path moveToPoint:target]; - for (NSUInteger i = 0; i < vertex.count; i += 1) { + for (NSUInteger i = 0; i < vertex.count; ++i) { previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; point = (vertex[i]).pointValue; nextPoint = (vertex[(i+1)%vertex.count]).pointValue; @@ -428,7 +515,7 @@ - (void) drawViewWith:(NSArray*)candidateRanges } void xyTranslation(NSMutableArray *shape, CGFloat dx, CGFloat dy) { - for (NSUInteger i = 0; i < shape.count; i += 1) { + for (NSUInteger i = 0; i < shape.count; ++i) { NSPoint point = (shape[i]).pointValue; point.x += dx; point.y += dy; @@ -443,7 +530,8 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = _textView.layoutManager; + NSLayoutManager *layoutManager = self.textView.layoutManager; + NSTextContainer *textContainer = self.textView.textContainer; NSSize edgeInset = self.currentTheme.edgeInset; *leadingRect = NSZeroRect; *bodyRect = NSZeroRect; @@ -451,27 +539,45 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; - NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_textView.textContainer]; + NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; - if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { + NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; + if (NSMaxRange(leadingLineRange) > NSMaxRange(glyphRange)) { *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, endPoint.x - startPoint.x, NSHeight(leadingLineRect)); } else { - CGFloat rightEdge = MAX(NSMaxX(leadingLineRect), NSMaxX(boundingRect)); - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - self.currentTheme.hilitedCornerRadius, NSMaxX(boundingRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange]; - CGFloat leftEdge = MIN(NSMinX(trailingLineRect), NSMinX(boundingRect)); - *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, - NSMinX(trailingLineRect) - leftEdge + endPoint.x, NSHeight(trailingLineRect)); - NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); - if (bodyLineRange.length > 0) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, - rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); + NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; + CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + self.currentTheme.hilitedCornerRadius, NSMinX(boundingRect)); + if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { + if (glyphRange.location == leadingLineRange.location) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); + } else { + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); + } + } else { + *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, + NSMinX(trailingLineRect) + endPoint.x - leftEdge, NSHeight(trailingLineRect)); + if (glyphRange.location == leadingLineRange.location) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); + } else { + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); + if (bodyLineRange.length > 0) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); + } + } } } } @@ -506,41 +612,25 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } } -// If the point is outside the innerBox, will extend to reach the outerBox -void expand(NSMutableArray *vertex, NSRect innerBorder, NSRect outerBorder) { - for (NSUInteger i = 0; i < vertex.count; i += 1) { - NSPoint point = [vertex[i] pointValue]; - if (point.x < innerBorder.origin.x) { - point.x = outerBorder.origin.x; - } else if (point.x > innerBorder.origin.x+innerBorder.size.width) { - point.x = outerBorder.origin.x+outerBorder.size.width; - } - if (point.y < innerBorder.origin.y) { - point.y = outerBorder.origin.y; - } else if (point.y > innerBorder.origin.y+innerBorder.size.height) { - point.y = outerBorder.origin.y+outerBorder.size.height; - } - [vertex replaceObjectAtIndex:i withObject:@(point)]; +NSColor *hooverColor(NSColor *color, BOOL darkTheme) { + if (@available(macOS 10.14, *)) { + return [color colorWithSystemEffect:NSColorSystemEffectRollover]; + } + if (darkTheme) { + return [color highlightWithLevel:0.3]; + } else { + return [color shadowWithLevel:0.3]; } } -// If the point is outside the boundBox, will shrink to the border of boundBox -void shrink(NSMutableArray *vertex, NSRect boundBox) { - for (NSUInteger i = 0; i < vertex.count; i += 1) { - NSPoint point = [vertex[i] pointValue]; - if (point.x < NSMinX(boundBox)) { - point.x = NSMinX(boundBox); - } - if (point.y < NSMinY(boundBox)) { - point.y = NSMinY(boundBox); - } - if (point.x > NSMaxX(boundBox)) { - point.x = NSMaxX(boundBox); - } - if (point.y > NSMaxY(boundBox)) { - point.y = NSMaxY(boundBox); - } - [vertex replaceObjectAtIndex:i withObject:@(point)]; +NSColor *disabledColor(NSColor *color, BOOL darkTheme) { + if (@available(macOS 10.14, *)) { + return [color colorWithSystemEffect:NSColorSystemEffectDisabled]; + } + if (darkTheme) { + return [color shadowWithLevel:0.3]; + } else { + return [color highlightWithLevel:0.3]; } } @@ -548,232 +638,246 @@ void shrink(NSMutableArray *vertex, NSRect boundBox) { - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *backgroundPath; NSBezierPath *borderPath; + NSBezierPath *textContainerPath; NSBezierPath *highlightedPath; NSBezierPath *highlightedPreeditPath; - NSBezierPath *nonCandidateBlockPath; NSBezierPath *candidateBlockPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; SquirrelTheme *theme = self.currentTheme; [NSBezierPath setDefaultLineWidth:0]; + NSRect backgroundRect = dirtyRect; - NSRect textField = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); - - // Draw preedit Rect + NSRect textContainer = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); + + // perform typesetting to get vertically centered layout and get the block rect + NSPoint lineOrigin = NSZeroPoint; NSRect preeditRect = NSZeroRect; if (_preeditRange.length > 0) { - preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.width = backgroundRect.size.width; - preeditRect.size.height += theme.edgeInset.height + theme.preeditLinespace/2; - preeditRect.origin = backgroundRect.origin; - if (_highlightedIndex == NSNotFound) { - preeditRect.size.height += theme.edgeInset.height - theme.preeditLinespace/2; - } + NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); + preeditRect = [self setLineRectForRange:_preeditRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) paragraphStyle:theme.preeditParagraphStyle]; + lineOrigin.y = NSMaxY(preeditRect); } + NSRect candidateBlockRect = NSZeroRect; + if (_candidateRanges.count > 0) { + CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], MAX([theme.commentAttrs[NSFontAttributeName] pointSize], [theme.labelAttrs[NSFontAttributeName] pointSize])); + NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); + NSRange candidateBlockRange = NSUnionRange(_candidateRanges[0].rangeValue, theme.linear && _pagingRange.length >0 ? _pagingRange : _candidateRanges[_candidateRanges.count-1].rangeValue); + candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; + lineOrigin.y = NSMaxY(candidateBlockRect); + } + NSRect pagingLineRect = NSZeroRect; + if (!theme.linear && _pagingRange.length > 0) { + pagingLineRect = [self setLineRectForRange:_pagingRange atOrigin:lineOrigin withReferenceFont:theme.pagingAttrs[NSFontAttributeName] paragraphStyle:theme.pagingParagraphStyle]; + lineOrigin.y = NSMaxY(pagingLineRect); + } + [self.textView.layoutManager setExtraLineFragmentRect:NSMakeRect(lineOrigin.x, lineOrigin.y, NSWidth(textContainer), NSHeight(textContainer)) usedRect:NSMakeRect(lineOrigin.x, lineOrigin.y, theme.hilitedCornerRadius*2, theme.paragraphStyle.minimumLineHeight) textContainer:self.textView.textContainer]; - // Draw paging Rect - NSRect pagingRect = NSZeroRect; - if (_pagingRange.length > 0) { - if (!theme.linear) { - pagingRect = [self contentRectForRange:_pagingRange]; - pagingRect.size.width = backgroundRect.size.width; - pagingRect.size.height += theme.edgeInset.height; - pagingRect.origin.x = backgroundRect.origin.x; - pagingRect.origin.y += theme.edgeInset.height; - } - NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 2)]; - pageUpRect.origin.y += theme.edgeInset.height; - pageUpRect.origin.x += theme.edgeInset.width; - _pagingRects[0] = [NSValue valueWithRect:pageUpRect]; - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-2, 2)]; - pageDownRect.origin.y += theme.edgeInset.height; - pageDownRect.origin.x += theme.edgeInset.width; - _pagingRects[1] = [NSValue valueWithRect:pageDownRect]; - if (theme.highlightedPreeditColor != nil) { - pageUpPath = [NSBezierPath bezierPathWithRoundedRect:pageUpRect xRadius:pageUpRect.size.height * 0.2 yRadius:pageUpRect.size.width * 0.2]; - pageDownPath = [NSBezierPath bezierPathWithRoundedRect:pageDownRect xRadius:pageDownRect.size.height * 0.2 yRadius:pageDownRect.size.width * 0.2]; - } + // Draw preedit Rect + if (_preeditRange.length > 0) { + preeditRect.size.width = textContainer.size.width; + preeditRect.origin = textContainer.origin; } // Draw candidate Rect - if (_highlightedIndex != NSNotFound) { - NSRect outerBox = NSInsetRect(backgroundRect, theme.edgeInset.width, 0); - outerBox.size.height -= preeditRect.size.height + pagingRect.size.height; - outerBox.origin.y += preeditRect.size.height; - if (_preeditRange.length == 0) { - outerBox.size.height -= theme.edgeInset.height; - outerBox.origin.y += theme.edgeInset.height; - } - if (_pagingRange.length == 0 || theme.linear) { - outerBox.size.height -= theme.edgeInset.height; - } - NSRect innerBox = NSInsetRect(outerBox, 0, theme.linespace/2); + if (_candidateRanges.count > 0) { + candidateBlockRect.size.width = textContainer.size.width; + candidateBlockRect.origin.x = textContainer.origin.x; + candidateBlockRect.origin.y += theme.edgeInset.height; if (theme.preeditBackgroundColor != nil) { - candidateBlockPath = drawSmoothLines(rectVertex(outerBox), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + candidateBlockPath = drawSmoothLines(rectVertex(candidateBlockRect), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); } // Draw candidate highlight rect if (theme.linear) { - for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + CGFloat highlightPadding = MIN(_seperatorWidth/2, theme.hilitedCornerRadius); + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { NSRange candidateRange = _candidateRanges[i].rangeValue; NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, 0); NSMutableArray *candidatePoints; NSMutableArray *candidatePoints2; // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { candidatePoints = [rectVertex(leadingRect) mutableCopy]; candidatePoints2 = [rectVertex(trailingRect) mutableCopy]; } else { candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - - expand(candidatePoints, innerBox, outerBox); NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (candidatePoints2.count > 0) { - expand(candidatePoints2, innerBox, outerBox); [candidatePath appendBezierPath:drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } _candidatePaths[i] = candidatePath; } } else { - for (NSUInteger i = 0; i < _candidateRanges.count; i += 1) { + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { NSRange candidateRange = _candidateRanges[i].rangeValue; NSRect candidateRect = [self contentRectForRange:candidateRange]; - candidateRect.size.width = textField.size.width; - candidateRect.origin.x = textField.origin.x; - candidateRect.origin.y += theme.edgeInset.height; + candidateRect.size.width = textContainer.size.width; + candidateRect.origin.x = textContainer.origin.x; NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; - - expand(candidatePoints, innerBox, outerBox); NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; } } - if (theme.highlightedStripColor != nil) { - highlightedPath = _candidatePaths[_highlightedIndex]; - } } // Draw highlighted part of preedit text if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect innerBox = NSInsetRect(preeditRect, theme.edgeInset.width, theme.edgeInset.height); - if (_highlightedIndex != NSNotFound) { - innerBox.size.height -= theme.preeditLinespace/2 - theme.edgeInset.height; - } + NSRect innerBox = NSInsetRect(preeditRect, theme.hilitedCornerRadius, 0); + innerBox.size.height -= theme.preeditLinespace; NSRect leadingRect; NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSIntersectionRect(leadingRect, innerBox); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSIntersectionRect(bodyRect, innerBox); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSIntersectionRect(trailingRect, innerBox); NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; } else { highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - shrink(highlightedPreeditPoints, innerBox); highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (highlightedPreeditPoints2.count > 0) { - shrink(highlightedPreeditPoints2, innerBox); [highlightedPreeditPath appendBezierPath:drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; } } - NSRect borderRect = NSInsetRect(backgroundRect, theme.edgeInset.width/2 - theme.borderWidth/2, theme.edgeInset.height/2 - theme.borderWidth/2); - borderPath = drawSmoothLines(rectVertex(borderRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); - borderPath.lineWidth = theme.borderWidth; + // Draw paging Rect + if (_pagingRange.length > 0) { + CGFloat buttonPadding = theme.linear ? MIN(_seperatorWidth/2, theme.hilitedCornerRadius) : theme.hilitedCornerRadius; + NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; + pageUpRect.origin.x -= buttonPadding; + pageUpRect.size.width += buttonPadding; + pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*NSHeight(pageUpRect), 0.28*NSWidth(pageUpRect)); + _pagingPaths[0] = pageUpPath; + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; + pageDownRect.size.width += buttonPadding; + pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); + _pagingPaths[1] = pageDownPath; + } + + // Draw borders backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); + textContainerPath = drawSmoothLines(rectVertex(textContainer), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { + borderPath = [backgroundPath copy]; + [borderPath appendBezierPath:textContainerPath]; + borderPath.windingRule = NSEvenOddWindingRule; + } - // set layers + // set layers] _shape.path = [backgroundPath quartzPath]; - self.layer.contentsFormat = kCAContentsFormatRGBA16Float; + _shape.fillColor = [[NSColor whiteColor] CGColor]; + CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; + textContainerLayer.path = [textContainerPath quartzPath]; + textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; [self.layer setSublayers: NULL]; - self.layer.cornerRadius = theme.cornerRadius; - if (@available(macOS 10.15, *)) { - self.layer.cornerCurve = kCACornerCurveContinuous; - } + CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; if (theme.backgroundImage) { - self.layer.backgroundColor = [theme.backgroundImage CGColor]; + panelLayer.backgroundColor = [theme.backgroundImage CGColor]; } - CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; - panelLayer.path = [backgroundPath quartzPath]; + panelLayer.path = [textContainerPath quartzPath]; panelLayer.fillColor = [theme.backgroundColor CGColor]; [self.layer addSublayer:panelLayer]; + if (@available(macOS 10.14, *)) { + if (theme.translucency > 0) { + panelLayer.opacity = 1.0 - theme.translucency; + } + } if (theme.preeditBackgroundColor && - (_preeditRange.length > 0 || _highlightedIndex != NSNotFound)) { - CAShapeLayer *nonCandidateLayer = [[CAShapeLayer alloc] init]; - nonCandidateBlockPath = [backgroundPath copy]; + (_preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { + panelLayer.fillColor = [theme.preeditBackgroundColor CGColor]; if (![candidateBlockPath isEmpty]) { - [nonCandidateBlockPath appendBezierPath:candidateBlockPath]; - [nonCandidateBlockPath setWindingRule:NSEvenOddWindingRule]; - nonCandidateLayer.fillRule = kCAFillRuleEvenOdd; + CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; + candidateLayer.path = [candidateBlockPath quartzPath]; + candidateLayer.fillColor = [theme.backgroundColor CGColor]; + [panelLayer addSublayer:candidateLayer]; } - nonCandidateLayer.path = [nonCandidateBlockPath quartzPath]; - nonCandidateLayer.fillColor = [theme.preeditBackgroundColor CGColor]; - [panelLayer addSublayer:nonCandidateLayer]; } + if (theme.borderColor && ![borderPath isEmpty]) { + CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; + borderLayer.path = [borderPath quartzPath]; + borderLayer.fillColor = [theme.borderColor CGColor]; + borderLayer.fillRule = kCAFillRuleEvenOdd; + [panelLayer addSublayer:borderLayer]; + } + CIFilter *backColorFilter = [CIFilter filterWithName:@"CISourceATopCompositing"]; + panelLayer.compositingFilter = backColorFilter; if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; + highlightedPath = _candidatePaths[_highlightedIndex]; highlightedLayer.path = [highlightedPath quartzPath]; highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; - [panelLayer addSublayer:highlightedLayer]; + CAShapeLayer *candidateMaskLayer = [[CAShapeLayer alloc] init]; + candidateMaskLayer.path = [candidateBlockPath quartzPath]; + candidateMaskLayer.fillColor = [[NSColor whiteColor] CGColor]; + highlightedLayer.mask = candidateMaskLayer; + [self.layer addSublayer:highlightedLayer]; + } + NSColor *buttonColor = theme.linear ? theme.highlightedStripColor : theme.highlightedPreeditColor; + if (_pagingRange.length > 0 && buttonColor) { + CAShapeLayer *pagingLayer = [[CAShapeLayer alloc] init]; + switch (_pagingButton) { + case NSPageUpFunctionKey: { + pagingLayer.path = [pageUpPath quartzPath]; + pagingLayer.fillColor = [hooverColor(buttonColor, self.isDark) CGColor]; + } break; + case NSBeginFunctionKey: { + pagingLayer.path = [pageUpPath quartzPath]; + pagingLayer.fillColor = [disabledColor(buttonColor, self.isDark) CGColor]; + } break; + case NSPageDownFunctionKey: { + pagingLayer.path = [pageDownPath quartzPath]; + pagingLayer.fillColor = [hooverColor(buttonColor, self.isDark) CGColor]; + } break; + case NSEndFunctionKey: { + pagingLayer.path = [pageDownPath quartzPath]; + pagingLayer.fillColor = [disabledColor(buttonColor, self.isDark) CGColor]; + } break; + } + pagingLayer.mask = textContainerLayer; + [self.layer addSublayer:pagingLayer]; } if (theme.highlightedPreeditColor) { - if (_pagingRange.length > 0) { - CAShapeLayer *pageUpLayer = [[CAShapeLayer alloc] init]; - pageUpLayer.path = [pageUpPath quartzPath]; - pageUpLayer.fillColor = nil; - CAShapeLayer *pageDownLayer = [[CAShapeLayer alloc] init]; - pageDownLayer.path = [pageDownPath quartzPath]; - pageDownLayer.fillColor = nil; - if (_pagingButton == NSPageUpFunctionKey) { - pageUpLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; - } if (_pagingButton == NSBeginFunctionKey) { - pageUpLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; - } else if (_pagingButton == NSPageDownFunctionKey) { - pageDownLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; - } else if (_pagingButton == NSEndFunctionKey) { - pageDownLayer.fillColor = [[theme.highlightedPreeditColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; - } - [panelLayer addSublayer:pageUpLayer]; - [panelLayer addSublayer:pageDownLayer]; - } if(![highlightedPreeditPath isEmpty]) { CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; - [panelLayer addSublayer:highlightedPreeditLayer]; + highlightedPreeditLayer.mask = textContainerLayer; + [self.layer addSublayer:highlightedPreeditLayer]; } } - if (theme.borderColor && (theme.borderWidth > 0)) { - CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; - borderLayer.path = [borderPath quartzPath]; - borderLayer.lineWidth = theme.borderWidth; - borderLayer.fillColor = nil; - borderLayer.strokeColor = [theme.borderColor CGColor]; - [panelLayer addSublayer:borderLayer]; - } - [_textView setTextContainerInset:theme.edgeInset]; + [self.textView setTextContainerInset:theme.edgeInset]; + // get sharp emojis on non-retina screens + [self.textView.layer setContentsScale:self.window.backingScaleFactor*3]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { if (NSPointInRect(spot, self.bounds)) { - if (_pagingRects[0] != nil && NSPointInRect(spot, _pagingRects[0].rectValue)) { - *index = NSPageUpFunctionKey; - return YES; - } else if (_pagingRects[1] != nil && NSPointInRect(spot, _pagingRects[1].rectValue)) { - *index = NSPageDownFunctionKey; - return YES; + if (_pagingPaths.count > 0) { + if ([_pagingPaths[0] containsPoint:spot]) { + *index = NSPageUpFunctionKey; // borrow function-key unicode for readability + return YES; + } + if ([_pagingPaths[1] containsPoint:spot]) { + *index = NSPageDownFunctionKey; // borrow function-key unicode for readability + return YES; + } } - for (NSUInteger i = 0; i < _candidatePaths.count; i++) { + for (NSUInteger i = 0; i < _candidatePaths.count; ++i) { if ([_candidatePaths[i] containsPoint:spot]) { *index = i; return YES; @@ -801,7 +905,7 @@ @implementation SquirrelPanel { NSArray *_labels; NSUInteger _index; NSUInteger _pageNum; - NSInteger _turnPage; + NSUInteger _turnPage; BOOL _lastPage; BOOL _mouseDown; NSPoint _scrollLocus; @@ -834,32 +938,6 @@ - (BOOL)rememberSize { return _view.currentTheme.rememberSize; } -void fixDefaultFont(NSMutableAttributedString *text, BOOL vertical) { - [text fixFontAttributeInRange:NSMakeRange(0, text.length)]; - NSRange currentFontRange = NSMakeRange(NSNotFound, 0); - NSUInteger i = 0; - while (i < text.length) { - NSFont *charFont = [text attribute:NSFontAttributeName atIndex:i effectiveRange:¤tFontRange]; - if ([charFont.fontName isEqualToString:@"AppleColorEmoji"]) { - NSFont *defaultFont = [NSFont systemFontOfSize:charFont.pointSize]; - [text addAttribute:NSFontAttributeName value:defaultFont range:currentFontRange]; - if (charFont.pointSize < 24 && vertical) { - NSRect const emojiRect = [defaultFont boundingRectForGlyph:[defaultFont glyphWithName:@"u2B1B"]]; // ⬛ - NSRect const defaultRect = [defaultFont boundingRectForGlyph:[defaultFont glyphWithName:@"uni25A0"]]; // ■ - [text addAttribute:NSBaselineOffsetAttributeName value:@((ceil(NSMaxY(defaultRect)) - ceil(NSMaxY(emojiRect)))/2) range:currentFontRange]; - if (currentFontRange.location == 0) { - NSMutableDictionary *attrs = [[text attributesAtIndex:0 effectiveRange:NULL] mutableCopy]; - attrs[NSKernAttributeName] = @(ceil(NSMaxY(emojiRect) - NSMaxY(defaultRect))); - [text insertAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:attrs] atIndex:0]; - } else { - [text addAttribute:NSKernAttributeName value:@(ceil(NSMaxY(emojiRect) - NSMaxY(defaultRect))) range:NSMakeRange(currentFontRange.location-1, 1)]; - } - } - } - i = NSMaxRange(currentFontRange); - } -} - + (NSColor *)secondaryTextColor { if(@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -868,62 +946,93 @@ + (NSColor *)secondaryTextColor { } } ++ (NSColor *)accentColor { + if (@available(macOS 10.14, *)) { + return [NSColor controlAccentColor]; + } else { + return [NSColor colorForControlTint:[[self class] currentControlTint]]; + } +} + - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; theme.native = YES; theme.candidateFormat = kDefaultCandidateFormat; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; + NSColor *accentColor = [[self class] accentColor]; + NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; + NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; + NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:kDefaultFontSize]; + + NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; + defaultAttrs[NSLigatureAttributeName] = [NSNumber numberWithInt:0]; + defaultAttrs[NSVerticalGlyphFormAttributeName] = [NSNumber numberWithBool:theme.vertical]; - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; - attrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + attrs[NSFontAttributeName] = userFont; - NSMutableDictionary *highlightedAttrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *highlightedAttrs = [defaultAttrs mutableCopy]; highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedMenuItemTextColor]; - highlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + highlightedAttrs[NSFontAttributeName] = userFont; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; - labelAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; - labelAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; + labelAttrs[NSForegroundColorAttributeName] = accentColor; + labelAttrs[NSFontAttributeName] = userMonoFont; NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; labelHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; - labelHighlightedAttrs[NSFontAttributeName] = [NSFont userFixedPitchFontOfSize:kDefaultFontSize]; + labelHighlightedAttrs[NSFontAttributeName] = userMonoFont; - NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *commentAttrs = [defaultAttrs mutableCopy]; commentAttrs[NSForegroundColorAttributeName] = secondaryTextColor; - commentAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + commentAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *commentHighlightedAttrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *commentHighlightedAttrs = [defaultAttrs mutableCopy]; commentHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; - commentHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + commentHighlightedAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *preeditAttrs = [defaultAttrs mutableCopy]; preeditAttrs[NSForegroundColorAttributeName] = [NSColor textColor]; - preeditAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + preeditAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *preeditHighlightedAttrs = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *preeditHighlightedAttrs = [defaultAttrs mutableCopy]; preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedTextColor]; - preeditHighlightedAttrs[NSFontAttributeName] = [NSFont userFontOfSize:kDefaultFontSize]; + preeditHighlightedAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; - pagingAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; - pagingAttrs[NSFontAttributeName] = [NSFont fontWithName:@"AppleSymbols" size:kDefaultFontSize]; + NSMutableDictionary *pagingAttrs = [defaultAttrs mutableCopy]; + pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? accentColor : [NSColor controlTextColor]; + pagingAttrs[NSFontAttributeName] = symbolFont; - NSParagraphStyle *preeditParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; - NSParagraphStyle *paragraphStyle = [NSParagraphStyle defaultParagraphStyle]; - NSParagraphStyle *pagingParagraphStyle = [NSParagraphStyle defaultParagraphStyle]; + NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; + pagingHighlightedAttrs[NSFontAttributeName] = symbolFont; + + NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + + preeditParagraphStyle.lineBreakMode = NSLineBreakByCharWrapping; + preeditParagraphStyle.alignment = NSTextAlignmentLeft; + paragraphStyle.alignment = NSTextAlignmentLeft; + pagingParagraphStyle.alignment = NSTextAlignmentLeft; + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left + // characters from setting the writing direction in case the label are direction-less symbols + preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs]; + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle pagingParagraphStyle:pagingParagraphStyle]; @@ -967,38 +1076,35 @@ - (instancetype)init { } - (void)sendEvent:(NSEvent *)event { - BOOL handled = NO; switch (event.type) { case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: { - NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) { _index = cursorIndex; _mouseDown = YES; - handled = YES; } } } break; case NSEventTypeLeftMouseUp: { - NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index && ((cursorIndex >= 0 && cursorIndex < _candidates.count) || - cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey)) { - handled = [self.inputController perform:kSELECT onIndex:cursorIndex]; + if (cursorIndex == _index || cursorIndex == _turnPage) { + [self.inputController perform:kSELECT onIndex:cursorIndex]; _mouseDown = NO; } } } break; case NSEventTypeRightMouseUp: { - NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { - handled = [self.inputController perform:kDELETE onIndex:cursorIndex]; + [self.inputController perform:kDELETE onIndex:cursorIndex]; _mouseDown = NO; } } @@ -1010,31 +1116,27 @@ - (void)sendEvent:(NSEvent *)event { self.acceptsMouseMovedEvents = NO; } break; case NSEventTypeMouseMoved: { - NSPoint spot = [_view convertPoint:[self mouseLocationOutsideOfEventStream] fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { [self.inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; - handled = YES; - } else if (cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey || - cursorIndex == NSBeginFunctionKey || cursorIndex == NSEndFunctionKey) { // borrow corresponding unicodes for readability + } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { + _turnPage = cursorIndex; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; - handled = YES; } } } break; case NSEventTypeLeftMouseDragged: { _mouseDown = NO; [self performWindowDragWithEvent:event]; - handled = YES; } break; case NSEventTypeScrollWheel: { CGFloat scrollThreshold = [_view.currentTheme.attrs[NSParagraphStyleAttributeName] minimumLineHeight]; if (event.phase == NSEventPhaseBegan) { _scrollLocus = NSZeroPoint; - handled = YES; } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && _scrollLocus.x != NSNotFound && _scrollLocus.y != NSNotFound) { // determine scrolling direction by confining to sectors within ±30º of any axis @@ -1045,16 +1147,16 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - handled = [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { - handled = [self.inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; + [self.inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - handled = [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { - handled = [self.inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; + [self.inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } } @@ -1062,13 +1164,13 @@ - (void)sendEvent:(NSEvent *)event { default: break; } - if (!handled) [super sendEvent:event]; + [super sendEvent:event]; } - (void)getCurrentScreen { // get current screen NSScreen *currentScreen = [NSScreen mainScreen]; - _screenRect = [currentScreen visibleFrame]; + _screenRect = currentScreen.visibleFrame; NSArray *screens = [NSScreen screens]; for (NSUInteger i = 0; i < screens.count; ++i) { if (NSPointInRect(_position.origin, [screens[i] frame])) { @@ -1078,6 +1180,13 @@ - (void)getCurrentScreen { } } +- (void)getMaxTextWidth { + SquirrelTheme *theme = _view.currentTheme; + [self getCurrentScreen]; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + _maxTextWidth = (theme.vertical ? NSHeight(_screenRect) : NSWidth(_screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2; +} + // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { SquirrelTheme *theme = _view.currentTheme; @@ -1090,27 +1199,28 @@ - (void)show { } //Break line if the text is too long, based on screen size. + _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; CGFloat textWidth = _view.textView.textStorage.size.width; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - CGFloat maxTextHeight = theme.vertical ? (NSWidth(_screenRect) - theme.edgeInset.width * 2) : (NSHeight(_screenRect) - theme.edgeInset.height * 2); + CGFloat maxTextHeight = (theme.vertical ? NSWidth(_screenRect) : NSHeight(_screenRect)) - theme.edgeInset.height * 2; if (textWidth > _maxTextWidth) { textWidth = _maxTextWidth; } - _view.textView.textContainer.size = NSMakeSize(textWidth, maxTextHeight); + _view.textView.textContainer.size = NSMakeSize(textWidth + theme.hilitedCornerRadius * 2, maxTextHeight); bool sweepVertical = NSWidth(_position) > NSHeight(_position); - NSRect contentRect = _view.contentRect; + NSRect contentRect = NSInsetRect(_view.contentRect, theme.hilitedCornerRadius, 0); NSRect maxContentRect = contentRect; // remember panel size (fix the top leading anchor of the panel in screen coordiantes) if (theme.rememberSize) { if (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : (sweepVertical ? (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) : - (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.edgeInset.width > NSMaxX(_screenRect)))) { + (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(_screenRect)))) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - _view.textView.textContainer.size = NSMakeSize(_maxSize.width, maxTextHeight); + _view.textView.textContainer.size = NSMakeSize(_maxSize.width + theme.hilitedCornerRadius * 2, maxTextHeight); } } if (theme.vertical ? (NSMinX(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : @@ -1127,10 +1237,10 @@ - (void)show { NSRect windowRect; if (theme.vertical) { windowRect.size = NSMakeSize(NSHeight(maxContentRect) + theme.edgeInset.height * 2, - NSWidth(maxContentRect) + theme.edgeInset.width * 2); + NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width : -kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width+theme.hilitedCornerRadius : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } @@ -1138,10 +1248,10 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height; + windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height - theme.preeditLinespace; } } else { - windowRect.size = NSMakeSize(NSWidth(maxContentRect) + theme.edgeInset.width * 2, + windowRect.size = NSMakeSize(NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2, NSHeight(maxContentRect) + theme.edgeInset.height * 2); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa @@ -1152,7 +1262,7 @@ - (void)show { } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width, + windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width - theme.hilitedCornerRadius, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } @@ -1191,9 +1301,9 @@ - (void)show { [_view setFrame:self.contentView.bounds]; [_view.textView setFrame:self.contentView.bounds]; - BOOL translucency = theme.translucency; + CGFloat translucency = theme.translucency; if (@available(macOS 10.14, *)) { - if (translucency) { + if (translucency > 0) { [_back setFrame:self.contentView.bounds]; [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; @@ -1228,9 +1338,6 @@ - (void)showPreedit:(NSString *)preedit lastPage:(BOOL)lastPage turnPage:(NSUInteger)turnPage update:(BOOL)update { - if (update == NSNotFound) { - update = _index != index && _pageNum == pageNum && _lastPage == lastPage ? NO : YES; - } if (update) { _preedit = preedit; _selRange = selRange; @@ -1247,18 +1354,11 @@ - (void)showPreedit:(NSString *)preedit _index = index = NSNotFound; } - switch(turnPage) { - case NSPageUpFunctionKey: - case NSBeginFunctionKey: { - _turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } break; - case NSPageDownFunctionKey: - case NSEndFunctionKey: { - _turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; - } break; - default: { - _turnPage = NSNotFound; - } break; + _turnPage = turnPage; + if (_turnPage == NSPageUpFunctionKey) { + turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; + } else if (_turnPage == NSPageDownFunctionKey) { + turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; } if (numCandidates || (preedit && preedit.length)) { @@ -1278,9 +1378,7 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; - [self getCurrentScreen]; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _maxTextWidth = theme.vertical ? (NSHeight(_screenRect) * textWidthRatio - theme.edgeInset.height * 2) : (NSWidth(_screenRect) * textWidthRatio - theme.edgeInset.width * 2); + [self getMaxTextWidth]; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); @@ -1307,7 +1405,6 @@ - (void)showPreedit:(NSString *)preedit attributes:theme.preeditAttrs]]; } - fixDefaultFont(preeditLine, theme.vertical); [preeditLine addAttribute:NSParagraphStyleAttributeName value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; @@ -1322,16 +1419,16 @@ - (void)showPreedit:(NSString *)preedit // prepare paging and separator for width calculation but no insertion yet NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(theme.vertical ? (pageNum ? @"▲" : @"△") : (pageNum ? @"◀" : @"◁")) + attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]]; [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%lu", pageNum+1] attributes:theme.pagingAttrs]]; - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:(pageNum ? @"\u25C0\uFE0E" : @"\u25C1\uFE0E").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs] atIndex:0]; // ◀ and ◁ - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(lastPage ? @"\u25B7\uFE0E" : @"\u25B6\uFE0E").precomposedStringWithCanonicalMapping - attributes:theme.pagingAttrs]]; // ▷ and ▶ + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(theme.vertical ? (lastPage ? @"▽" : @"▼") : (lastPage ? @"▷" : @"▶")) + attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]]; - fixDefaultFont(paging, theme.vertical); CGFloat pagingWidth = theme.showPaging ? NSWidth([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; CGFloat separatorWidth = theme.linear ? NSWidth([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + _view.seperatorWidth = separatorWidth; CGFloat lineWidth = 0.0 - separatorWidth; CGFloat maxLineWidth = MIN(preeditWidth, _maxTextWidth); BOOL useTab = NO; @@ -1339,7 +1436,7 @@ - (void)showPreedit:(NSString *)preedit // candidates NSUInteger candidateBlockStart = text.length; NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; - for (NSUInteger i = 0; i < candidates.count; i += 1) { + for (NSUInteger i = 0; i < candidates.count; ++i) { NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; @@ -1349,17 +1446,17 @@ - (void)showPreedit:(NSString *)preedit if (theme.prefixLabelFormat != nil) { NSString *prefixLabelString; + NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; if (labels.count > 1 && i < labels.count) { - NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCompatibilityMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... - char labelCharacter = [labels[0] characterAtIndex:i]; - prefixLabelString = [[NSString stringWithFormat:theme.prefixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... - NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - prefixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString attributes:labelAttrs]]; @@ -1380,38 +1477,37 @@ - (void)showPreedit:(NSString *)preedit [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:commentAttrs]]; NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCanonicalMapping + [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCompatibilityMapping attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { NSString *suffixLabelString; + NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; if (labels.count > 1 && i < labels.count) { - NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... - char labelCharacter = [labels[0] characterAtIndex:i]; - suffixLabelString = [[NSString stringWithFormat:theme.suffixLabelFormat, labelCharacter] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... - NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%lu"]; - suffixLabelString = [[NSString stringWithFormat:labelFormat, (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString attributes:attrs]]; + [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString attributes:labelAttrs]]; } - fixDefaultFont(line, theme.vertical); NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; // determine if the line is too wide and line break is needed, based on screen size. - NSString *separtatorString = @"\u2029"; + NSString *separtatorString = @"\n"; CGFloat candidateWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); if (theme.linear) { if (i == numCandidates-1) { candidateWidth += separatorWidth + pagingWidth; } if (lineWidth + separatorWidth + candidateWidth > _maxTextWidth) { - separtatorString = @"\u2028"; + separtatorString = @"\n"; maxLineWidth = MAX(maxLineWidth, MIN(lineWidth, _maxTextWidth)); lineWidth = candidateWidth; } else { @@ -1451,8 +1547,8 @@ - (void)showPreedit:(NSString *)preedit } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if (useTab) { - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:paging.length-2]; - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:2]; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:paging.length-1]; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:1]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; } @@ -1467,12 +1563,12 @@ - (void)showPreedit:(NSString *)preedit [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; - _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + [_view.textView setLayoutOrientation: theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; // text done! - [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:_turnPage]; + [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:turnPage]; - if (update) [self show]; + [self show]; } - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { @@ -1497,8 +1593,6 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; - [text addAttribute:NSKernAttributeName value:@(0) range:NSMakeRange(0, text.length)]; - fixDefaultFont(text, theme.vertical); [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; @@ -1545,22 +1639,35 @@ - (void)hideStatus:(NSTimer *)timer { // If the font name is not valid, NSFontDescriptor will still create something for us. // However, when we draw the actual text, Squirrel will crash if there is any font descriptor // with invalid font name. - [validFontDescriptors addObject:[NSFontDescriptor fontDescriptorWithName:fontName size:0.0]]; + NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithName:fontName size:0.0]; + NSFontDescriptor *UIFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized]; + [validFontDescriptors addObject:([NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil ? UIFontDescriptor : fontDescriptor)]; } } if (validFontDescriptors.count == 0) { return nil; - } else if (validFontDescriptors.count == 1) { - return validFontDescriptors[0]; } + CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef) @"zh"); + NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); + CFRelease(systemFontRef); + NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - NSArray *fallbackDescriptors = [validFontDescriptors - subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)]; - NSDictionary *attributes = @{NSFontCascadeListAttribute : fallbackDescriptors}; + NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + NSDictionary *attributes = @{NSFontCascadeListAttribute:fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } +static CGFloat getLineHeight(NSFont *font) { + CGFloat lineHeight = font.ascender - font.descender; + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); + } + return lineHeight; +} + static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfig *config, NSString *prefix) { NSString* candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { @@ -1602,7 +1709,6 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f updateTextOrientation(&vertical, config, @"style"); BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; - BOOL translucency = [config getBool:@"style/translucency"]; BOOL showPaging = [config getBool:@"style/show_paging"]; BOOL rememberSize = [config getBool:@"style/remember_size"]; NSString *statusMessageType = [config getString:@"style/status_message_type"]; @@ -1615,6 +1721,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSString *commentFontName = [config getString:@"style/comment_font_face"]; CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; CGFloat alpha = MIN(MAX([config getDouble:@"style/alpha"], 0.0), 1.0); + CGFloat translucency = MIN(MAX([config getDouble:@"style/translucency"], 0.0), 1.0); CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; CGFloat borderHeight = [config getDouble:@"style/border_height"]; @@ -1622,7 +1729,6 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; - CGFloat kerning = [config getDouble:@"style/kerning"]; NSColor *backgroundColor; NSColor *backgroundImage; @@ -1693,10 +1799,6 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f if (inlineCandidateOverridden) { inlineCandidate = inlineCandidateOverridden.boolValue; } - NSNumber *translucencyOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/translucency"]]; - if (translucencyOverridden) { - translucency = translucencyOverridden.boolValue; - } NSNumber *showPagingOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]]; if (showPagingOverridden) { showPaging = showPagingOverridden.boolValue; @@ -1705,7 +1807,6 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f if (candidateFormatOverridden) { candidateFormat = candidateFormatOverridden; } - NSString *fontNameOverridden = [config getString:[prefix stringByAppendingString:@"/font_face"]]; if (fontNameOverridden) { fontName = fontNameOverridden; @@ -1735,7 +1836,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f candidateLabelColor = candidateLabelColorOverridden; } NSColor *highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (!highlightedCandidateLabelColorOverridden) { + if (highlightedCandidateLabelColorOverridden == nil) { // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; } @@ -1744,7 +1845,11 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; if (alphaOverridden) { - alpha = fmin(fmax(alphaOverridden.doubleValue, 0.0), 1.0); + alpha = MIN(MAX(alphaOverridden.doubleValue, 0.0), 1.0); + } + NSNumber *translucencyOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"]]; + if (translucencyOverridden) { + translucency = MIN(MAX(translucencyOverridden.doubleValue, 0.0), 1.0); } NSNumber *cornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]]; if (cornerRadiusOverridden) { @@ -1774,86 +1879,42 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } - NSNumber *kerningOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/kerning"]]; - if (kerningOverridden) { - kerning = kerningOverridden.doubleValue; - } } - if (fontSize == 0) { // default size - fontSize = kDefaultFontSize; - } - if (labelFontSize == 0) { - labelFontSize = fontSize; - } - if (commentFontSize == 0) { - commentFontSize = fontSize; - } - NSFontDescriptor *fontDescriptor = nil; - NSFont *font = nil; - if (fontName != nil) { - fontDescriptor = getFontDescriptor(fontName); - if (fontDescriptor != nil) { - font = [NSFont fontWithDescriptor:fontDescriptor size:fontSize]; - } - } - if (font == nil) { // use default font - font = [NSFont userFontOfSize:fontSize]; - } - NSFontDescriptor *labelFontDescriptor = nil; - NSFont *labelFont = nil; - if (labelFontName != nil) { - labelFontDescriptor = getFontDescriptor(labelFontName); - if (labelFontDescriptor != nil) { - labelFont = [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize]; - } - } - if (labelFont == nil) { - labelFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; - } - NSFontDescriptor *commentFontDescriptor = nil; - NSFont *commentFont = nil; - if (commentFontName != nil) { - commentFontDescriptor = getFontDescriptor(commentFontName); - if (commentFontDescriptor == nil) { - commentFontDescriptor = fontDescriptor; - } - if (commentFontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:commentFontDescriptor size:commentFontSize]; - } - } - if (commentFont == nil) { - if (fontDescriptor != nil) { - commentFont = [NSFont fontWithDescriptor:fontDescriptor size:commentFontSize]; - } else { - commentFont = [NSFont fontWithName:font.fontName size:commentFontSize]; - } - } - NSFont *pagingFont = [NSFont fontWithName:@"AppleSymbols" size:labelFontSize]; + fontSize = fontSize ? fontSize : kDefaultFontSize; + labelFontSize = labelFontSize ? labelFontSize : fontSize; + commentFontSize = commentFontSize ? commentFontSize : fontSize; - CGFloat fontHeight = font.ascender - font.descender; - CGFloat commentFontHeight = commentFont.ascender - commentFont.descender; - CGFloat labelFontHeight = labelFont.ascender - labelFont.descender; - CGFloat lineHeight = MAX(fontHeight, MAX(commentFontHeight, labelFontHeight)); + NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); + NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? fontDescriptor : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) size:fontSize]; - NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - preeditParagraphStyle.alignment = NSTextAlignmentLeft; - preeditParagraphStyle.minimumLineHeight = fontHeight; - preeditParagraphStyle.paragraphSpacing = spacing / 2; + NSFontDescriptor *labelFontDescriptor = getFontDescriptor(labelFontName); + NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] : (fontDescriptor ? [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize] : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]); - NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - paragraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.minimumLineHeight = lineHeight + lineSpacing / 2; - paragraphStyle.lineSpacing = lineSpacing / 2; + NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); + NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? commentFontDescriptor : fontDescriptor) size:commentFontSize]; - NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - pagingParagraphStyle.alignment = NSTextAlignmentLeft; + NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:labelFontSize]; - // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left - // characters from setting the writing direction in case the label are direction-less symbols - paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + CGFloat fontHeight = getLineHeight(font); + CGFloat labelFontHeight = getLineHeight(labelFont); + CGFloat commentFontHeight = getLineHeight(commentFont); + CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); + + NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; + preeditParagraphStyle.minimumLineHeight = fontHeight; + preeditParagraphStyle.maximumLineHeight = fontHeight; + preeditParagraphStyle.paragraphSpacing = spacing; + + NSMutableParagraphStyle *paragraphStyle = [theme.paragraphStyle mutableCopy]; + paragraphStyle.minimumLineHeight = lineHeight; + paragraphStyle.maximumLineHeight = lineHeight; + paragraphStyle.paragraphSpacing = lineSpacing / 2; + paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + + NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; + pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; + pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -1864,6 +1925,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSMutableDictionary *preeditAttrs = [theme.preeditAttrs mutableCopy]; NSMutableDictionary *preeditHighlightedAttrs = [theme.preeditHighlightedAttrs mutableCopy]; NSMutableDictionary *pagingAttrs = [theme.pagingAttrs mutableCopy]; + NSMutableDictionary *pagingHighlightedAttrs = [theme.pagingHighlightedAttrs mutableCopy]; attrs[NSFontAttributeName] = font; highlightedAttrs[NSFontAttributeName] = font; @@ -1874,36 +1936,33 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; pagingAttrs[NSFontAttributeName] = pagingFont; + pagingHighlightedAttrs[NSFontAttributeName] = pagingFont; + attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + commentAttrs[NSBaselineOffsetAttributeName] =@(baseOffset); commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.2)); - attrs[NSKernAttributeName] = @(kerning); - highlightedAttrs[NSKernAttributeName] = @(kerning); - labelAttrs[NSKernAttributeName] = @(kerning); - labelHighlightedAttrs[NSKernAttributeName] = @(kerning); - commentAttrs[NSKernAttributeName] = @(kerning); - commentHighlightedAttrs[NSKernAttributeName] = @(kerning); - preeditAttrs[NSKernAttributeName] = @(kerning); - preeditHighlightedAttrs[NSKernAttributeName] = @(kerning); - pagingAttrs[NSKernAttributeName] = @(kerning); + pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.2)); + + preeditAttrs[NSLigatureAttributeName] = @0; + preeditHighlightedAttrs[NSLigatureAttributeName] = @0; NSColor *secondaryTextColor = [[self class] secondaryTextColor]; + NSColor *accentColor = [[self class] accentColor]; + backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; borderColor = borderColor ? borderColor : isNative ? [NSColor gridColor] : nil; preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : isNative ? [NSColor windowBackgroundColor] : nil; candidateTextColor = candidateTextColor ? candidateTextColor : [NSColor controlTextColor]; highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedMenuItemTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : isNative ? [NSColor selectedContentBackgroundColor] : nil; - candidateLabelColor = candidateLabelColor ? candidateLabelColor : - isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); - highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : - isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); + highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : isNative ? [NSColor alternateSelectedControlColor] : nil; + candidateLabelColor = candidateLabelColor ? candidateLabelColor : isNative ? accentColor : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); + highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); commentTextColor = commentTextColor ? commentTextColor : secondaryTextColor; highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : [NSColor alternateSelectedControlTextColor]; textColor = textColor ? textColor : [NSColor textColor]; @@ -1911,36 +1970,39 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f highlightedBackColor = highlightedBackColor ? highlightedBackColor : isNative ? [NSColor selectedTextBackgroundColor] : nil; attrs[NSForegroundColorAttributeName] = candidateTextColor; - labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; + labelAttrs[NSForegroundColorAttributeName] = candidateLabelColor; labelHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateLabelColor; commentAttrs[NSForegroundColorAttributeName] = commentTextColor; commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; preeditAttrs[NSForegroundColorAttributeName] = textColor; preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; - pagingAttrs[NSForegroundColorAttributeName] = candidateLabelColor; - - attrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - labelAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - highlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - commentAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - preeditAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; - pagingAttrs[NSVerticalGlyphFormAttributeName] = vertical ? @YES : @NO; + pagingAttrs[NSForegroundColorAttributeName] = linear ? candidateLabelColor : candidateTextColor; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedCandidateTextColor; + + attrs[NSVerticalGlyphFormAttributeName] = @(vertical); + labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + highlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + commentAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + preeditAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + pagingAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); [theme setStatusMessageType:statusMessageType]; [theme setAttrs:attrs - labelAttrs:labelAttrs highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs labelHighlightedAttrs:labelHighlightedAttrs commentAttrs:commentAttrs commentHighlightedAttrs:commentHighlightedAttrs preeditAttrs:preeditAttrs preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs]; + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle @@ -1953,14 +2015,11 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - borderHeight = MAX(borderHeight, cornerRadius - cornerRadius / sqrt(2)); - borderWidth = MAX(borderWidth, cornerRadius - cornerRadius / sqrt(2)); NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); [theme setCornerRadius:cornerRadius - hilitedCornerRadius:hilitedCornerRadius + hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/2) edgeInset:edgeInset - borderWidth:MAX(borderHeight, borderWidth) linespace:lineSpacing preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) @@ -1973,7 +2032,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f inlineCandidate:inlineCandidate]; theme.native = isNative; - theme.candidateFormat = (candidateFormat ? candidateFormat : kDefaultCandidateFormat); + theme.candidateFormat = candidateFormat ? candidateFormat : kDefaultCandidateFormat; } @end diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 9ac7d90d4..66d3f0104 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -39,10 +39,8 @@ style: line_spacing: 5 # space between preedit and candidates in non-inline mode spacing: 10 - # space betweeen letters and characters, a.k.a. tracking - kerning: 1 - #candidate_format: '%c. %@' + #candidate_format: '%c %@' # adjust the base line of vertical text #base_offset: 6 @@ -253,8 +251,8 @@ preset_color_schemes: inline_preedit: true # 单行显示,false双行显示 candidate_format: "%c\u2005%@\u2005" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 corner_radius: 2 # 候选条圆角 - border_height: 3 # 窗口边界高度,大于圆角半径×(1-1/√2)才生效 - border_width: 8 # 窗口边界宽度,大于圆角半径×(1-1/√2)才生效 + border_height: 3 # 窗口边界高度,大于圆角半径才生效 + border_width: 8 # 窗口边界宽度,大于圆角半径才生效 back_color: 0xeeffffff # 候选条背景色 border_color: 0xE0B693 # 边框色 font_face: "HYQiHei-55S Book,HanaMinA Regular" # 候选词字体 @@ -279,8 +277,8 @@ preset_color_schemes: candidate_format: "%c\u2005%@" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。 corner_radius: 5 # 候选条圆角 hilited_corner_radius: 3 # 高亮圆角 - border_height: 6 # 窗口边界高度,大于圆角半径×(1-1/√2)才生效 - border_width: 6 # 窗口边界宽度,大于圆角半径×(1-1/√2)才生效 + border_height: 6 # 窗口边界高度,大于圆角半径才生效 + border_width: 6 # 窗口边界宽度,大于圆角半径才生效 font_face: "PingFangSC" # 候选词字体 font_point: 16 # 候选字词大小 label_font_point: 14 # 候选编号大小 From 2ec2869d8489f2450d6bd25d2de247a9f99b80d6 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 20 Jun 2023 10:59:11 +0200 Subject: [PATCH 093/164] originalString composedString --- SquirrelInputController.m | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 13940adb5..f3b570181 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -345,13 +345,8 @@ -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { - if ([_preeditString isEqualToString:@"\u3000"]) { - const char* raw_input = rime_get_api()->get_input(_session); - if (raw_input) - [self commitString:@(raw_input)]; - } else { // inline mode - [self commitString:_preeditString]; - } + NSString *composition = [self composedString:sender]; + [self commitString:composition]; rime_get_api()->clear_composition(_session); } } @@ -390,6 +385,30 @@ -(NSMenu*)menu return NSApp.squirrelAppDelegate.menu; } +- (NSAttributedString *)originalString:(id)sender +{ + const char* raw_input = rime_get_api()->get_input(_session); + NSAttributedString *originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; + return originalString; +} + +- (id)composedString:(id)sender +{ + RIME_STRUCT(RimeContext, ctx); + const char *preedit = ctx.composition.preedit; + if (!preedit) + return @""; + if (rime_get_api()->get_option(_session, "soft_cursor")) { + int caretPos = ctx.composition.cursor_pos; + char composed[strlen(preedit)-3]; + for (int i = 0; i < strlen(preedit)-3; ++i) { + composed[i] = preedit[i < caretPos ? i : i+3]; + } + return @(composed); + } + return @(preedit); +} + -(NSArray*)candidates:(id)sender { return _candidates; From 7f92234be2d0b22a77e25dc95eb33e31ce5e36f8 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 20 Jun 2023 10:59:27 +0200 Subject: [PATCH 094/164] autobuild --- autobuild.sh | 19 +++++++++++++++++++ librime | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 autobuild.sh diff --git a/autobuild.sh b/autobuild.sh new file mode 100644 index 000000000..fc6e18594 --- /dev/null +++ b/autobuild.sh @@ -0,0 +1,19 @@ +make clean clean-deps + +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua + +git submodules update --init --recursive + +export BUILD_UNIVERSAL=1 + +make -C librime xcode/deps/boost + +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" + +export BUILD_UNIVERSAL=1 + +make deps + +make + +make install \ No newline at end of file diff --git a/librime b/librime index 32958f7b5..c63099eb5 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 32958f7b5cbaa3d4ed61e7deb410c1995b3ab39b +Subproject commit c63099eb5e8372e5b2968830784daf791a996060 From dca568a82ce9e9e936f1a59035eac9ab062b354c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 05:02:25 +0200 Subject: [PATCH 095/164] baseline offset --- SquirrelPanel.m | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index f6cb50a6c..778c80c6f 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -355,7 +355,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; rect.origin.y = NSMaxY(blockRect); usedRect.origin.y = NSMaxY(blockRect); - CGFloat alignment = verticalLayout ? lineHeight/2 : (refBaseline + lineHeight/2 - refFontHeight/2); + CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline; rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); if (style.lineSpacing > 0) { @@ -382,6 +382,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; + CGFloat baselineOffset = [[textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL] doubleValue]; NSRange runRange = NSIntersectionRange(fontRunRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { runFont = runFont.verticalFont; @@ -392,7 +393,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange CGFloat resizedRefFontHeight = [layoutManager defaultLineHeightForFont:resizedRefFont]; CGFloat resizedRefBaseline = [layoutManager defaultBaselineOffsetForFont:resizedRefFont]; CGFloat runFontOvershoot = MAX(0.0, runFontHeight - runBaseline - resizedRefFontHeight + resizedRefBaseline)/2; - runGlyphPosition.y = alignment + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; if (verticalLayout) { if (runFont.verticalFont.isVertical) { runGlyphPosition.x += runFontOvershoot; @@ -758,14 +759,14 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw paging Rect if (_pagingRange.length > 0) { CGFloat buttonPadding = theme.linear ? MIN(_seperatorWidth/2, theme.hilitedCornerRadius) : theme.hilitedCornerRadius; + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; + pageDownRect.size.width += buttonPadding; + pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; pageUpRect.origin.x -= buttonPadding; - pageUpRect.size.width += buttonPadding; + pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*NSHeight(pageUpRect), 0.28*NSWidth(pageUpRect)); _pagingPaths[0] = pageUpPath; - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; - pageDownRect.size.width += buttonPadding; - pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); _pagingPaths[1] = pageDownPath; } @@ -1563,7 +1564,7 @@ - (void)showPreedit:(NSString *)preedit [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; - [_view.textView setLayoutOrientation: theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; // text done! [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:turnPage]; @@ -1596,7 +1597,7 @@ - (void)showStatus:(NSString *)message { [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; - _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; NSRange emptyRange = NSMakeRange(NSNotFound, 0); _maxSize = NSZeroSize; // disable remember_size for status messages @@ -1942,12 +1943,12 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentAttrs[NSBaselineOffsetAttributeName] =@(baseOffset); + commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.2)); - pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.2)); + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.1)); + pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.1)); preeditAttrs[NSLigatureAttributeName] = @0; preeditHighlightedAttrs[NSLigatureAttributeName] = @0; From 45eef93a5be439eafb3a1490356a17132763e873 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 16:31:44 +0200 Subject: [PATCH 096/164] fix a bug which ignores space before first line --- SquirrelPanel.m | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 778c80c6f..a7785df08 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -413,9 +413,9 @@ - (NSRect)setLineRectForRange:(NSRange)charRange // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; - NSRect rect = [self.textView.layoutManager usedRectForTextContainer:self.textView.textContainer]; + NSRect firstLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:0 effectiveRange:NULL]; NSRect extraLineRect = [self.textView.layoutManager extraLineFragmentRect]; - rect.size.height -= NSHeight(extraLineRect); + NSRect rect = NSMakeRect(NSMinX(firstLineRect), NSMaxY(firstLineRect), NSWidth(firstLineRect), NSMinY(extraLineRect)-NSMaxY(firstLineRect)); return rect; } @@ -663,10 +663,14 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect candidateBlockRect = NSZeroRect; if (_candidateRanges.count > 0) { CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], MAX([theme.commentAttrs[NSFontAttributeName] pointSize], [theme.labelAttrs[NSFontAttributeName] pointSize])); - NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); + NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); NSRange candidateBlockRange = NSUnionRange(_candidateRanges[0].rangeValue, theme.linear && _pagingRange.length >0 ? _pagingRange : _candidateRanges[_candidateRanges.count-1].rangeValue); candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; lineOrigin.y = NSMaxY(candidateBlockRect); + } else if (_preeditRange.length == 0) { // status message + NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); + candidateBlockRect = [self setLineRectForRange:NSMakeRange(1, self.textView.textStorage.length-2) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:[NSParagraphStyle defaultParagraphStyle]]; + lineOrigin.y = NSMaxY(candidateBlockRect); } NSRect pagingLineRect = NSZeroRect; if (!theme.linear && _pagingRange.length > 0) { @@ -1381,7 +1385,7 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; [self getMaxTextWidth]; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]; // trick to force space before first line NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); CGFloat preeditWidth = 0.0; @@ -1398,7 +1402,7 @@ - (void)showPreedit:(NSString *)preedit [preeditLine appendAttributedString:[[NSAttributedString alloc] initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping attributes:theme.preeditHighlightedAttrs]]; - highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); + highlightedPreeditRange = NSMakeRange(text.length + highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); } if (NSMaxRange(selRange) < preedit.length) { [preeditLine appendAttributedString:[[NSAttributedString alloc] @@ -1410,8 +1414,8 @@ - (void)showPreedit:(NSString *)preedit value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; + preeditRange = NSMakeRange(text.length, preeditLine.length); [text appendAttributedString:preeditLine]; - preeditRange = NSMakeRange(0, text.length); preeditWidth = NSWidth([preeditLine boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); if (numCandidates) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; @@ -1593,7 +1597,9 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; From 625a63e5b755b489c4c03a4ee0c9b00311bb707d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 17:48:34 +0200 Subject: [PATCH 097/164] clean up --- SquirrelPanel.m | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a7785df08..275d98b9d 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -391,12 +391,10 @@ - (NSRect)setLineRectForRange:(NSRange)charRange CGFloat runBaseline = [layoutManager defaultBaselineOffsetForFont:runFont]; CGFloat runFontHeight = [layoutManager defaultLineHeightForFont:runFont]; CGFloat resizedRefFontHeight = [layoutManager defaultLineHeightForFont:resizedRefFont]; - CGFloat resizedRefBaseline = [layoutManager defaultBaselineOffsetForFont:resizedRefFont]; - CGFloat runFontOvershoot = MAX(0.0, runFontHeight - runBaseline - resizedRefFontHeight + resizedRefBaseline)/2; - runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/4; if (verticalLayout) { if (runFont.verticalFont.isVertical) { - runGlyphPosition.x += runFontOvershoot; + runGlyphPosition.x += MAX(0.0, runFontHeight - resizedRefFontHeight)/2; } else { runGlyphPosition.y += runBaseline - runFontHeight/2; } From 6fa58675afdcc6141f2feef0ad72898faa585d77 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 18:23:49 +0200 Subject: [PATCH 098/164] Update SquirrelPanel.m --- SquirrelPanel.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 275d98b9d..98dae79b2 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -355,7 +355,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; rect.origin.y = NSMaxY(blockRect); usedRect.origin.y = NSMaxY(blockRect); - CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline; + CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + lineHeight/2 - refFontHeight/2; rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); if (style.lineSpacing > 0) { @@ -390,8 +390,9 @@ - (NSRect)setLineRectForRange:(NSRange)charRange } CGFloat runBaseline = [layoutManager defaultBaselineOffsetForFont:runFont]; CGFloat runFontHeight = [layoutManager defaultLineHeightForFont:runFont]; + CGFloat resizedRefBaseline = [layoutManager defaultBaselineOffsetForFont:resizedRefFont]; CGFloat resizedRefFontHeight = [layoutManager defaultLineHeightForFont:resizedRefFont]; - runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/4; + runGlyphPosition.y = alignment - baselineOffset; if (verticalLayout) { if (runFont.verticalFont.isVertical) { runGlyphPosition.x += MAX(0.0, runFontHeight - resizedRefFontHeight)/2; From 59d31547cfcf2b84b051f522233c70894882de82 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 22:15:51 +0200 Subject: [PATCH 099/164] sharp emoji on non-retina --- SquirrelPanel.m | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 98dae79b2..d1c3d0acd 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -341,6 +341,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect blockRect = NSMakeRect(origin.x, origin.y, 0, 0); BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + NSRect refFontBBox = [refFont boundingRectForFont]; CGFloat refFontHeight = [layoutManager defaultLineHeightForFont:refFont]; CGFloat refBaseline = [layoutManager defaultBaselineOffsetForFont:refFont]; CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); @@ -355,7 +356,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; rect.origin.y = NSMaxY(blockRect); usedRect.origin.y = NSMaxY(blockRect); - CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + lineHeight/2 - refFontHeight/2; + CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + MAX(0.0, lineHeight - NSHeight(refFontBBox))/2; rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); if (style.lineSpacing > 0) { @@ -372,7 +373,11 @@ - (NSRect)setLineRectForRange:(NSRange)charRange usedRect.origin.y += style.paragraphSpacingBefore; alignment += style.paragraphSpacingBefore; } - [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:NSIntersectionRect(usedRect, rect)]; + blockRect = NSUnionRect(blockRect, rect); + usedRect = NSIntersectionRect(usedRect, rect); + rect = [self.textView backingAlignedRect:rect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + usedRect = [self.textView backingAlignedRect:usedRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:usedRect]; // typesetting glyphs NSRange fontRunRange = NSMakeRange(NSNotFound, 0); @@ -381,29 +386,28 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; - NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; + NSRect runFontBBox = [runFont boundingRectForFont]; CGFloat baselineOffset = [[textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL] doubleValue]; NSRange runRange = NSIntersectionRange(fontRunRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { runFont = runFont.verticalFont; - resizedRefFont = resizedRefFont.verticalFont; } CGFloat runBaseline = [layoutManager defaultBaselineOffsetForFont:runFont]; CGFloat runFontHeight = [layoutManager defaultLineHeightForFont:runFont]; - CGFloat resizedRefBaseline = [layoutManager defaultBaselineOffsetForFont:resizedRefFont]; - CGFloat resizedRefFontHeight = [layoutManager defaultLineHeightForFont:resizedRefFont]; - runGlyphPosition.y = alignment - baselineOffset; if (verticalLayout) { - if (runFont.verticalFont.isVertical) { - runGlyphPosition.x += MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - NSHeight(runFontBBox))/4; + if (runFont.isVertical) { + runGlyphPosition.x += MAX(0.0, runFontHeight - NSHeight(runFontBBox))/2; } else { runGlyphPosition.y += runBaseline - runFontHeight/2; } + } else { + runGlyphPosition.y = alignment - baselineOffset; } - [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; + NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesInward|NSAlignRectFlipped)]; + [layoutManager setLocation:NSMakePoint(NSWidth(lineDrawnRect), NSHeight(lineDrawnRect)) forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } - blockRect = NSUnionRect(blockRect, rect); i = NSMaxRange(lineRange); } return blockRect; @@ -865,8 +869,6 @@ - (void)drawRect:(NSRect)dirtyRect { } } [self.textView setTextContainerInset:theme.edgeInset]; - // get sharp emojis on non-retina screens - [self.textView.layer setContentsScale:self.window.backingScaleFactor*3]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { From 311e34a5a40407ac98df2e63c430956b0665b813 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 22 Jun 2023 22:32:23 +0200 Subject: [PATCH 100/164] Update SquirrelPanel.m --- SquirrelPanel.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d1c3d0acd..0bbdf4655 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -404,7 +404,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange } else { runGlyphPosition.y = alignment - baselineOffset; } - NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesInward|NSAlignRectFlipped)]; + NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; [layoutManager setLocation:NSMakePoint(NSWidth(lineDrawnRect), NSHeight(lineDrawnRect)) forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } @@ -1454,7 +1454,7 @@ - (void)showPreedit:(NSString *)preedit NSString *prefixLabelString; NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; if (labels.count > 1 && i < labels.count) { - prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCompatibilityMapping; + prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; @@ -1483,7 +1483,7 @@ - (void)showPreedit:(NSString *)preedit [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " attributes:commentAttrs]]; NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCompatibilityMapping + [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCanonicalMapping attributes:commentAttrs]]; } From 93035e08672c00a0d82acf7322868e0fbb637294 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 23 Jun 2023 02:28:54 +0200 Subject: [PATCH 101/164] status paragraph style --- SquirrelPanel.m | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 0bbdf4655..20821e6b4 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -90,6 +90,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSParagraphStyle *paragraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; +@property(nonatomic, strong, readonly) NSParagraphStyle *statusParagraphStyle; @property(nonatomic, strong, readonly) NSString *prefixLabelFormat; @property(nonatomic, strong, readonly) NSString *suffixLabelFormat; @@ -132,7 +133,8 @@ - (void) setAttrs:(NSMutableDictionary *)attrs - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle - pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle; + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle + statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; @end @@ -238,10 +240,12 @@ - (void) setAttrs:(NSMutableDictionary *)attrs - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle - pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle { + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle + statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle{ _paragraphStyle = paragraphStyle; _preeditParagraphStyle = preeditParagraphStyle; _pagingParagraphStyle = pagingParagraphStyle; + _statusParagraphStyle = statusParagraphStyle; } @end @@ -672,7 +676,7 @@ - (void)drawRect:(NSRect)dirtyRect { lineOrigin.y = NSMaxY(candidateBlockRect); } else if (_preeditRange.length == 0) { // status message NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); - candidateBlockRect = [self setLineRectForRange:NSMakeRange(1, self.textView.textStorage.length-2) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:[NSParagraphStyle defaultParagraphStyle]]; + candidateBlockRect = [self setLineRectForRange:NSMakeRange(1, self.textView.textStorage.length-2) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:theme.statusParagraphStyle]; lineOrigin.y = NSMaxY(candidateBlockRect); } NSRect pagingLineRect = NSZeroRect; @@ -1018,16 +1022,20 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + NSMutableParagraphStyle *statusParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; preeditParagraphStyle.lineBreakMode = NSLineBreakByCharWrapping; preeditParagraphStyle.alignment = NSTextAlignmentLeft; paragraphStyle.alignment = NSTextAlignmentLeft; pagingParagraphStyle.alignment = NSTextAlignmentLeft; + statusParagraphStyle.alignment = NSTextAlignmentLeft; + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left // characters from setting the writing direction in case the label are direction-less symbols preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; [theme setAttrs:attrs highlightedAttrs:highlightedAttrs @@ -1041,7 +1049,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingHighlightedAttrs:pagingHighlightedAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle - pagingParagraphStyle:pagingParagraphStyle]; + pagingParagraphStyle:pagingParagraphStyle + statusParagraphStyle:statusParagraphStyle]; } - (instancetype)init { @@ -1598,11 +1607,14 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]; [text appendAttributedString:[[NSAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]]; [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; + [text addAttribute:NSParagraphStyleAttributeName value:theme.statusParagraphStyle range:NSMakeRange(0, text.length)]; [text fixAttributesInRange:NSMakeRange(0, text.length)]; + [_view.textView.textStorage setAttributedString:text]; [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; @@ -1924,6 +1936,10 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; + NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; + statusParagraphStyle.minimumLineHeight = commentFontHeight; + statusParagraphStyle.maximumLineHeight = commentFontHeight; + NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; NSMutableDictionary *labelAttrs = [theme.labelAttrs mutableCopy]; @@ -2014,7 +2030,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle - pagingParagraphStyle:pagingParagraphStyle]; + pagingParagraphStyle:pagingParagraphStyle + statusParagraphStyle:statusParagraphStyle]; [theme setBackgroundColor:backgroundColor backgroundImage:backgroundImage From 6fb5179794cb2f72cec802fd4aa2ba7ff2eb1c3d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 29 Jun 2023 05:27:14 +0200 Subject: [PATCH 102/164] TextKit2 --- SquirrelPanel.m | 725 +++++++++++++++++++++++++++++++----------------- 1 file changed, 478 insertions(+), 247 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 20821e6b4..b43d7a15d 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -216,16 +216,16 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs{ +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs{ _attrs = attrs; _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; @@ -263,12 +263,12 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSMutableArray *pagingPaths; @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; +@property(nonatomic, readonly) NSTextLayoutManager *layoutManager API_AVAILABLE(macos(12.0)); @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; +@property(nonatomic, getter=isFlipped, readonly) BOOL flipped; -- (BOOL)isFlipped; -@property (NS_NONATOMIC_IOSONLY, getter=isFlipped, readonly) BOOL flipped; - (void) drawViewWith:(NSArray *)candidateRanges highlightedIndex:(NSUInteger)highlightedIndex preeditRange:(NSRange)preeditRange @@ -276,6 +276,7 @@ - (void) drawViewWith:(NSArray *)candidateRanges pagingRange:(NSRange)pagingRange pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; +- (NSRect)contentRectForTextRange:(NSTextRange *)range API_AVAILABLE(macos(12.0)); - (NSRect)setLineRectForRange:(NSRange)charRange atOrigin:(NSPoint)origin withReferenceFont:(NSFont *)refFont @@ -316,16 +317,31 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.layer.masksToBounds = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; } - _textView = [[NSTextView alloc] initWithFrame:frameRect]; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSZeroSize]; - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - _textView.wantsLayer = NO; - [_textView replaceTextContainer:textContainer]; - _textView.layoutManager.backgroundLayoutEnabled = YES; - _textView.layoutManager.usesFontLeading = NO; - _textView.layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; + + if (@available(macOS 12.0, *)) { + _layoutManager = [[NSTextLayoutManager alloc] init]; + _layoutManager.usesFontLeading = NO; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + _layoutManager.textContainer = textContainer; + NSTextContentStorage *textStorage = [[NSTextContentStorage alloc] init]; + [textStorage addTextLayoutManager:_layoutManager]; + _textView = [[NSTextView alloc] initWithFrame:frameRect textContainer:_layoutManager.textContainer]; + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + _textView.wantsLayer = NO; + } else { + _textView = [[NSTextView alloc] initWithFrame:frameRect]; + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + _textView.wantsLayer = NO; + _textView.layoutManager.backgroundLayoutEnabled = YES; + _textView.layoutManager.usesFontLeading = NO; + _textView.layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + [_textView replaceTextContainer:textContainer]; + } _defaultTheme = [[SquirrelTheme alloc] init]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { @@ -334,6 +350,17 @@ - (instancetype)initWithFrame:(NSRect)frameRect { return self; } +- (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) { + if (range.location == NSNotFound) { + return nil; + } else { + NSTextContentManager *contentManager = self.layoutManager.textContentManager; + id startLocation = [contentManager locationFromLocation:contentManager.documentRange.location withOffset:range.location]; + id endLocation = [contentManager locationFromLocation:startLocation withOffset:range.length]; + return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; + } +} + - (NSRect)setLineRectForRange:(NSRange)charRange atOrigin:(NSPoint)origin withReferenceFont:(NSFont *)refFont @@ -345,9 +372,8 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRect blockRect = NSMakeRect(origin.x, origin.y, 0, 0); BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; - NSRect refFontBBox = [refFont boundingRectForFont]; - CGFloat refFontHeight = [layoutManager defaultLineHeightForFont:refFont]; - CGFloat refBaseline = [layoutManager defaultBaselineOffsetForFont:refFont]; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat refBaseline = refFont.ascender; CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; @@ -360,7 +386,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; rect.origin.y = NSMaxY(blockRect); usedRect.origin.y = NSMaxY(blockRect); - CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + MAX(0.0, lineHeight - NSHeight(refFontBBox))/2; + CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + MAX(0.0, lineHeight - refFontHeight)/2; rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); if (style.lineSpacing > 0) { @@ -372,7 +398,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange rect.size.height += style.paragraphSpacing; } if (style.paragraphSpacingBefore > 0 && (lineCharRange.location == 0 || - [textStorage.string characterAtIndex:lineCharRange.location-1] == '\n')) { + [textStorage.string characterAtIndex:lineCharRange.location-1] == '\n')) { rect.size.height += style.paragraphSpacingBefore; usedRect.origin.y += style.paragraphSpacingBefore; alignment += style.paragraphSpacingBefore; @@ -390,25 +416,27 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; - NSRect runFontBBox = [runFont boundingRectForFont]; + NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; CGFloat baselineOffset = [[textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL] doubleValue]; NSRange runRange = NSIntersectionRange(fontRunRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { runFont = runFont.verticalFont; + resizedRefFont = resizedRefFont.verticalFont; } - CGFloat runBaseline = [layoutManager defaultBaselineOffsetForFont:runFont]; - CGFloat runFontHeight = [layoutManager defaultLineHeightForFont:runFont]; + CGFloat runBaseline = runFont.ascender; + CGFloat runFontHeight = runFont.ascender - runFont.descender; + CGFloat resizedRefFontHeight = resizedRefFont.ascender - resizedRefFont.descender; if (verticalLayout) { - runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - NSHeight(runFontBBox))/4; + runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; if (runFont.isVertical) { - runGlyphPosition.x += MAX(0.0, runFontHeight - NSHeight(runFontBBox))/2; + runGlyphPosition.x += MAX(0.0, runFontHeight - resizedRefFontHeight)/2; } else { runGlyphPosition.y += runBaseline - runFontHeight/2; } } else { runGlyphPosition.y = alignment - baselineOffset; } - NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesInward|NSAlignRectFlipped)]; [layoutManager setLocation:NSMakePoint(NSWidth(lineDrawnRect), NSHeight(lineDrawnRect)) forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } @@ -419,22 +447,49 @@ - (NSRect)setLineRectForRange:(NSRange)charRange // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; - NSRect firstLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:0 effectiveRange:NULL]; - NSRect extraLineRect = [self.textView.layoutManager extraLineFragmentRect]; - NSRect rect = NSMakeRect(NSMinX(firstLineRect), NSMaxY(firstLineRect), NSWidth(firstLineRect), NSMinY(extraLineRect)-NSMaxY(firstLineRect)); - return rect; + NSRect contentRect; + if (@available(macOS 12.0, *)) { + [self.layoutManager ensureLayoutForRange:self.layoutManager.textContentManager.documentRange]; + contentRect = [self.layoutManager usageBoundsForTextContainer]; + } else { + [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; + contentRect = [self.textView.layoutManager usedRectForTextContainer:self.textView.textContainer]; + } + if (_candidateRanges.count > 0) { + if (_preeditRange.length == 0) { + contentRect.origin.y -= self.currentTheme.linespace/2; + contentRect.size.height += self.currentTheme.linespace/2; + } + if (self.currentTheme.linear || _pagingRange.length == 0) { + contentRect.size.height += self.currentTheme.linespace/2; + } + } + return contentRect; } // Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate +- (NSRect)contentRectForTextRange:(NSTextRange *)range API_AVAILABLE(macos(12.0)) { + __block NSRect contentRect = NSZeroRect; + [self.layoutManager enumerateTextSegmentsInRange:range type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + contentRect = NSUnionRect(contentRect, segmentRect); + return YES; + }]; + return contentRect; +} + - (NSRect)contentRectForRange:(NSRange)range { - NSSize edgeInset = self.currentTheme.edgeInset; - NSRange glyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [self.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textView.textContainer]; - NSRect firstLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:NULL]; - NSRect finalLineRect = [self.textView.layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; - return NSMakeRect(NSMinX(rect) + edgeInset.width, NSMinY(firstLineRect) + edgeInset.height, - NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + if (@available(macOS 12.0, *)) { + NSTextRange *textRange = [self getTextRangeFromRange:range]; + NSRect contentRect = [self contentRectForTextRange:textRange]; + return contentRect; + } else { + NSRange glyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect rect = [self.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textView.textContainer]; + NSRect firstLineRect = [self.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:NULL]; + NSRect finalLineRect = [self.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; + NSRect contentRect = NSMakeRect(NSMinX(rect), NSMinY(firstLineRect), NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + return contentRect; + } } // Will triger - (void)drawRect:(NSRect)dirtyRect @@ -471,8 +526,8 @@ - (void) drawViewWith:(NSArray*)candidateRanges NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) return path; - NSPoint previousPoint = (vertex[vertex.count-1]).pointValue; - NSPoint point = (vertex[0]).pointValue; + NSPoint previousPoint = vertex[vertex.count-1].pointValue; + NSPoint point = vertex[0].pointValue; NSPoint nextPoint; NSPoint control1; NSPoint control2; @@ -485,9 +540,9 @@ - (void) drawViewWith:(NSArray*)candidateRanges } [path moveToPoint:target]; for (NSUInteger i = 0; i < vertex.count; ++i) { - previousPoint = (vertex[(vertex.count+i-1)%vertex.count]).pointValue; - point = (vertex[i]).pointValue; - nextPoint = (vertex[(i+1)%vertex.count]).pointValue; + previousPoint = vertex[(vertex.count+i-1)%vertex.count].pointValue; + point = vertex[i].pointValue; + nextPoint = vertex[(i+1)%vertex.count].pointValue; target = point; control1 = point; diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); @@ -522,15 +577,6 @@ - (void) drawViewWith:(NSArray*)candidateRanges @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y))]; } -void xyTranslation(NSMutableArray *shape, CGFloat dx, CGFloat dy) { - for (NSUInteger i = 0; i < shape.count; ++i) { - NSPoint point = (shape[i]).pointValue; - point.x += dx; - point.y += dy; - shape[i] = @(point); - } -} - BOOL nearEmptyRect(NSRect rect) { return rect.size.height * rect.size.width < 1; } @@ -538,52 +584,87 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is complete lines in the middle - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSLayoutManager *layoutManager = self.textView.layoutManager; - NSTextContainer *textContainer = self.textView.textContainer; NSSize edgeInset = self.currentTheme.edgeInset; *leadingRect = NSZeroRect; *bodyRect = NSZeroRect; *trailingRect = NSZeroRect; - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; - NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; - NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; - NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; - if (NSMaxRange(leadingLineRange) > NSMaxRange(glyphRange)) { - *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, - endPoint.x - startPoint.x, NSHeight(leadingLineRect)); - } else { - CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - self.currentTheme.hilitedCornerRadius, NSMaxX(boundingRect)); - NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; - CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + self.currentTheme.hilitedCornerRadius, NSMinX(boundingRect)); - if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { - if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); - } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, - rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); + if (@available(macOS 12.0, *)) { + NSTextRange *textRange = [self getTextRangeFromRange:charRange]; + NSMutableArray *lineRects = [[NSMutableArray alloc] init]; + [self.layoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + if (!nearEmptyRect(segmentRect)) { + [lineRects addObject:[NSValue valueWithRect:NSOffsetRect(segmentRect, edgeInset.width, edgeInset.height)]]; } + return YES; + }]; + if (lineRects.count == 1) { + *bodyRect = lineRects[0].rectValue; } else { - *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, - NSMinX(trailingLineRect) + endPoint.x - leftEdge, NSHeight(trailingLineRect)); - if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); + NSRect firstLineRect = lineRects.firstObject.rectValue; + NSRect lastLineRect = lineRects.lastObject.rectValue; + if (NSMaxX(lastLineRect) == NSMaxX(firstLineRect)) { + if (NSMinX(firstLineRect) == NSMinX(lastLineRect)) { + *bodyRect = NSUnionRect(firstLineRect, lastLineRect); + } else { + *leadingRect = firstLineRect; + *bodyRect = NSUnionRect(lineRects[1].rectValue, lastLineRect); + } } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); - NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); - if (bodyLineRange.length > 0) { + *trailingRect = lastLineRect; + if (NSMinX(firstLineRect) == NSMinX(lastLineRect)) { + *bodyRect = NSUnionRect(firstLineRect, lineRects[lineRects.count-2].rectValue); + } else { + *leadingRect = firstLineRect; + if (lineRects.count > 2) { + *bodyRect = NSUnionRect(lineRects[1].rectValue, lineRects[lineRects.count-2].rectValue); + } + } + } + } + } else { + NSLayoutManager *layoutManager = self.textView.layoutManager; + NSTextContainer *textContainer = self.textView.textContainer; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; + NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; + NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; + NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); + NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; + if (NSMaxRange(leadingLineRange) > NSMaxRange(glyphRange)) { + *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + endPoint.x - startPoint.x, NSHeight(leadingLineRect)); + } else { + CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - self.currentTheme.hilitedCornerRadius, NSMaxX(boundingRect)); + NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); + NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; + CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + self.currentTheme.hilitedCornerRadius, NSMinX(boundingRect)); + if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { + if (glyphRange.location == leadingLineRange.location) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); + } else { + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, - rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); + rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); + } + } else { + *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, + NSMinX(trailingLineRect) + endPoint.x - leftEdge, NSHeight(trailingLineRect)); + if (glyphRange.location == leadingLineRange.location) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); + } else { + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + NSMinY(leadingLineRect) + edgeInset.height, + rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); + if (bodyLineRange.length > 0) { + *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); + } } } } @@ -652,50 +733,82 @@ - (void)drawRect:(NSRect)dirtyRect { NSBezierPath *candidateBlockPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; - SquirrelTheme *theme = self.currentTheme; - [NSBezierPath setDefaultLineWidth:0]; + SquirrelTheme *theme = self.currentTheme; + NSPoint lineOrigin = NSZeroPoint; NSRect backgroundRect = dirtyRect; - NSRect textContainer = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); - - // perform typesetting to get vertically centered layout and get the block rect - NSPoint lineOrigin = NSZeroPoint; + NSRect textContainerRect = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); NSRect preeditRect = NSZeroRect; - if (_preeditRange.length > 0) { - NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); - preeditRect = [self setLineRectForRange:_preeditRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) paragraphStyle:theme.preeditParagraphStyle]; - lineOrigin.y = NSMaxY(preeditRect); - } NSRect candidateBlockRect = NSZeroRect; - if (_candidateRanges.count > 0) { - CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], MAX([theme.commentAttrs[NSFontAttributeName] pointSize], [theme.labelAttrs[NSFontAttributeName] pointSize])); - NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); - NSRange candidateBlockRange = NSUnionRange(_candidateRanges[0].rangeValue, theme.linear && _pagingRange.length >0 ? _pagingRange : _candidateRanges[_candidateRanges.count-1].rangeValue); - candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; - lineOrigin.y = NSMaxY(candidateBlockRect); - } else if (_preeditRange.length == 0) { // status message - NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); - candidateBlockRect = [self setLineRectForRange:NSMakeRange(1, self.textView.textStorage.length-2) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:theme.statusParagraphStyle]; - lineOrigin.y = NSMaxY(candidateBlockRect); - } NSRect pagingLineRect = NSZeroRect; - if (!theme.linear && _pagingRange.length > 0) { - pagingLineRect = [self setLineRectForRange:_pagingRange atOrigin:lineOrigin withReferenceFont:theme.pagingAttrs[NSFontAttributeName] paragraphStyle:theme.pagingParagraphStyle]; - lineOrigin.y = NSMaxY(pagingLineRect); + + if (@available(macOS 12.0, *)) { + if (_preeditRange.length > 0) { + preeditRect = [self contentRectForRange:_preeditRange]; + preeditRect.size.height += theme.preeditLinespace; + preeditRect.origin = lineOrigin; + lineOrigin.y = NSMaxY(preeditRect); + } + if (_candidateRanges.count > 0) { + NSRange candidateBlockRange = NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue); + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); + candidateBlockRect.origin = lineOrigin; + lineOrigin.y = NSMaxY(candidateBlockRect); + } else if (_preeditRange.length == 0) { // status message + candidateBlockRect = [self contentRectForTextRange:self.layoutManager.textContentManager.documentRange]; + candidateBlockRect.origin = lineOrigin; + lineOrigin.y = NSMaxY(candidateBlockRect); + } + if (!theme.linear && _pagingRange.length > 0) { + pagingLineRect = [self contentRectForRange:_pagingRange]; + pagingLineRect.origin = lineOrigin; + lineOrigin.y = NSMaxY(pagingLineRect); + } + } else { + // perform typesetting to get vertically centered layout and get the block rect + if (_preeditRange.length > 0) { + NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); + preeditRect = [self setLineRectForRange:_preeditRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) paragraphStyle:theme.preeditParagraphStyle]; + lineOrigin.y = NSMaxY(preeditRect); + } + if (_candidateRanges.count > 0) { + CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], MAX([theme.commentAttrs[NSFontAttributeName] pointSize], [theme.labelAttrs[NSFontAttributeName] pointSize])); + NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); + NSRange candidateBlockRange = NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue); + candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; + lineOrigin.y = NSMaxY(candidateBlockRect); + } else if (_preeditRange.length == 0) { // status message + NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); + candidateBlockRect = [self setLineRectForRange:NSMakeRange(0, self.textView.textStorage.length) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:theme.statusParagraphStyle]; + lineOrigin.y = NSMaxY(candidateBlockRect); + } + if (!theme.linear && _pagingRange.length > 0) { + pagingLineRect = [self setLineRectForRange:_pagingRange atOrigin:lineOrigin withReferenceFont:theme.pagingAttrs[NSFontAttributeName] paragraphStyle:theme.pagingParagraphStyle]; + lineOrigin.y = NSMaxY(pagingLineRect); + } + } + if (_candidateRanges.count > 0) { + if (_preeditRange.length == 0) { + candidateBlockRect.origin.y -= theme.linespace/2; + candidateBlockRect.size.height += theme.linespace/2; + } + if (theme.linear || _pagingRange.length == 0) { + candidateBlockRect.size.height += theme.linespace/2; + } } - [self.textView.layoutManager setExtraLineFragmentRect:NSMakeRect(lineOrigin.x, lineOrigin.y, NSWidth(textContainer), NSHeight(textContainer)) usedRect:NSMakeRect(lineOrigin.x, lineOrigin.y, theme.hilitedCornerRadius*2, theme.paragraphStyle.minimumLineHeight) textContainer:self.textView.textContainer]; + [NSBezierPath setDefaultLineWidth:0]; // Draw preedit Rect if (_preeditRange.length > 0) { - preeditRect.size.width = textContainer.size.width; - preeditRect.origin = textContainer.origin; + preeditRect.size.width = textContainerRect.size.width; + preeditRect.origin = textContainerRect.origin; } // Draw candidate Rect if (_candidateRanges.count > 0) { - candidateBlockRect.size.width = textContainer.size.width; - candidateBlockRect.origin.x = textContainer.origin.x; + candidateBlockRect.size.width = textContainerRect.size.width; + candidateBlockRect.origin.x = textContainerRect.origin.x; candidateBlockRect.origin.y += theme.edgeInset.height; if (theme.preeditBackgroundColor != nil) { candidateBlockPath = drawSmoothLines(rectVertex(candidateBlockRect), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); @@ -710,9 +823,16 @@ - (void)drawRect:(NSRect)dirtyRect { NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, 0); + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, -theme.linespace/2); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, -theme.linespace/2); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, -theme.linespace/2); + if (@available(macOS 12.0, *)) { + if (_preeditRange.length == 0) { + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSOffsetRect(leadingRect, 0.0, theme.linespace/2); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSOffsetRect(bodyRect, 0.0, theme.linespace/2); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSOffsetRect(trailingRect, 0.0, theme.linespace/2); + } + } NSMutableArray *candidatePoints; NSMutableArray *candidatePoints2; // Handles the special case where containing boxes are separated @@ -731,9 +851,15 @@ - (void)drawRect:(NSRect)dirtyRect { } else { for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { NSRange candidateRange = _candidateRanges[i].rangeValue; - NSRect candidateRect = [self contentRectForRange:candidateRange]; - candidateRect.size.width = textContainer.size.width; - candidateRect.origin.x = textContainer.origin.x; + NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace/2); + candidateRect.size.width = textContainerRect.size.width; + candidateRect.origin.x = textContainerRect.origin.x; + candidateRect.origin.y += theme.edgeInset.height; + if (@available(macOS 12.0, *)) { + if (_preeditRange.length == 0) { + candidateRect = NSOffsetRect(candidateRect, 0.0, theme.linespace/2); + } + } NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; @@ -770,12 +896,22 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw paging Rect if (_pagingRange.length > 0) { CGFloat buttonPadding = theme.linear ? MIN(_seperatorWidth/2, theme.hilitedCornerRadius) : theme.hilitedCornerRadius; - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)]; + NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)], theme.edgeInset.width, theme.edgeInset.height); pageDownRect.size.width += buttonPadding; - pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); - NSRect pageUpRect = [self contentRectForRange:NSMakeRange(_pagingRange.location, 1)]; + NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(_pagingRange.location, 1)], theme.edgeInset.width, theme.edgeInset.height); pageUpRect.origin.x -= buttonPadding; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented + if (theme.linear) { + pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace/2); + pageUpRect = NSInsetRect(pageUpRect, 0.0, -theme.linespace/2); + } + if (@available(macOS 12.0, *)) { + if (_preeditRange.length == 0) { + pageDownRect = NSOffsetRect(pageDownRect, 0.0, theme.linespace/2); + pageUpRect = NSOffsetRect(pageUpRect, 0.0, theme.linespace/2); + } + } + pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*NSHeight(pageUpRect), 0.28*NSWidth(pageUpRect)); _pagingPaths[0] = pageUpPath; _pagingPaths[1] = pageDownPath; @@ -783,7 +919,7 @@ - (void)drawRect:(NSRect)dirtyRect { // Draw borders backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); - textContainerPath = drawSmoothLines(rectVertex(textContainer), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + textContainerPath = drawSmoothLines(rectVertex(textContainerRect), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { borderPath = [backgroundPath copy]; [borderPath appendBezierPath:textContainerPath]; @@ -903,7 +1039,7 @@ @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; - NSRect _screenRect; + NSScreen *_screen; NSSize _maxSize; CGFloat _maxTextWidth; @@ -974,10 +1110,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:kDefaultFontSize]; - NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; - defaultAttrs[NSLigatureAttributeName] = [NSNumber numberWithInt:0]; - defaultAttrs[NSVerticalGlyphFormAttributeName] = [NSNumber numberWithBool:theme.vertical]; NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; @@ -1006,10 +1139,12 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *preeditAttrs = [defaultAttrs mutableCopy]; preeditAttrs[NSForegroundColorAttributeName] = [NSColor textColor]; preeditAttrs[NSFontAttributeName] = userFont; + preeditAttrs[NSLigatureAttributeName] = @(0); NSMutableDictionary *preeditHighlightedAttrs = [defaultAttrs mutableCopy]; preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedTextColor]; preeditHighlightedAttrs[NSFontAttributeName] = userFont; + preeditHighlightedAttrs[NSLigatureAttributeName] = @(0); NSMutableDictionary *pagingAttrs = [defaultAttrs mutableCopy]; pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? accentColor : [NSColor controlTextColor]; @@ -1055,7 +1190,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:(NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless) + styleMask:(NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless) backing:NSBackingStoreBuffered defer:YES]; if (self) { @@ -1184,12 +1319,11 @@ - (void)sendEvent:(NSEvent *)event { - (void)getCurrentScreen { // get current screen - NSScreen *currentScreen = [NSScreen mainScreen]; - _screenRect = currentScreen.visibleFrame; + _screen = [NSScreen mainScreen]; NSArray *screens = [NSScreen screens]; for (NSUInteger i = 0; i < screens.count; ++i) { if (NSPointInRect(_position.origin, [screens[i] frame])) { - _screenRect = [screens[i] visibleFrame]; + _screen = screens[i]; break; } } @@ -1198,8 +1332,9 @@ - (void)getCurrentScreen { - (void)getMaxTextWidth { SquirrelTheme *theme = _view.currentTheme; [self getCurrentScreen]; + NSRect screenRect = [_screen visibleFrame]; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _maxTextWidth = (theme.vertical ? NSHeight(_screenRect) : NSWidth(_screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2; + _maxTextWidth = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2; } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect @@ -1214,28 +1349,31 @@ - (void)show { } //Break line if the text is too long, based on screen size. - _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; - CGFloat textWidth = _view.textView.textStorage.size.width; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - CGFloat maxTextHeight = (theme.vertical ? NSWidth(_screenRect) : NSHeight(_screenRect)) - theme.edgeInset.height * 2; - if (textWidth > _maxTextWidth) { - textWidth = _maxTextWidth; + NSTextContainer *textContainer; + if (@available(macOS 12.0, *)) { + textContainer = _view.layoutManager.textContainer; + } else { + textContainer = _view.textView.textContainer; } - _view.textView.textContainer.size = NSMakeSize(textWidth + theme.hilitedCornerRadius * 2, maxTextHeight); + [textContainer setLineFragmentPadding:theme.hilitedCornerRadius]; + CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + NSRect screenRect = [_screen visibleFrame]; + CGFloat maxTextHeight = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - theme.edgeInset.height * 2; + [textContainer setSize:NSMakeSize(_maxTextWidth + theme.hilitedCornerRadius * 2, maxTextHeight)]; bool sweepVertical = NSWidth(_position) > NSHeight(_position); - NSRect contentRect = NSInsetRect(_view.contentRect, theme.hilitedCornerRadius, 0); - NSRect maxContentRect = contentRect; + NSRect contentRect = _view.contentRect; + NSRect maxContentRect = NSInsetRect(contentRect, theme.hilitedCornerRadius, 0); // remember panel size (fix the top leading anchor of the panel in screen coordiantes) if (theme.rememberSize) { - if (theme.vertical ? (NSMinY(_position) / NSHeight(_screenRect) <= textWidthRatio) : - (sweepVertical ? (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) : - (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(_screenRect)))) { + if (theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) : + (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : + (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(screenRect)))) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - _view.textView.textContainer.size = NSMakeSize(_maxSize.width + theme.hilitedCornerRadius * 2, maxTextHeight); + [textContainer setSize:NSMakeSize(_maxSize.width + theme.hilitedCornerRadius * 2, maxTextHeight)]; } } if (theme.vertical ? (NSMinX(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : @@ -1254,7 +1392,7 @@ - (void)show { windowRect.size = NSMakeSize(NSHeight(maxContentRect) + theme.edgeInset.height * 2, NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) / NSHeight(_screenRect) > textWidthRatio) { + if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width+theme.hilitedCornerRadius : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); @@ -1263,14 +1401,14 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height - theme.preeditLinespace; + windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height; } } else { windowRect.size = NSMakeSize(NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2, NSHeight(maxContentRect) + theme.edgeInset.height * 2); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) / NSWidth(_screenRect) > textWidthRatio) { + if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); } else { windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; @@ -1282,17 +1420,17 @@ - (void)show { } } - if (NSMaxX(windowRect) > NSMaxX(_screenRect)) { - windowRect.origin.x = (sweepVertical ? NSMinX(_position)-kOffsetHeight : NSMaxX(_screenRect)) - NSWidth(windowRect); + if (NSMaxX(windowRect) > NSMaxX(screenRect)) { + windowRect.origin.x = (sweepVertical ? NSMinX(_position)-kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); } - if (NSMinX(windowRect) < NSMinX(_screenRect)) { - windowRect.origin.x = sweepVertical ? NSMaxX(_position)+kOffsetHeight : NSMinX(_screenRect); + if (NSMinX(windowRect) < NSMinX(screenRect)) { + windowRect.origin.x = sweepVertical ? NSMaxX(_position)+kOffsetHeight : NSMinX(screenRect); } - if (NSMinY(windowRect) < NSMinY(_screenRect)) { - windowRect.origin.y = sweepVertical ? NSMinY(_screenRect) : NSMaxY(_position)+kOffsetHeight; + if (NSMinY(windowRect) < NSMinY(screenRect)) { + windowRect.origin.y = sweepVertical ? NSMinY(screenRect) : NSMaxY(_position)+kOffsetHeight; } - if (NSMaxY(windowRect) > NSMaxY(_screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMaxY(_screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); + if (NSMaxY(windowRect) > NSMaxY(screenRect)) { + windowRect.origin.y = (sweepVertical ? NSMaxY(screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); } if (theme.vertical) { @@ -1302,7 +1440,8 @@ - (void)show { windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); } - [self setFrame:NSIntegralRectWithOptions(windowRect, NSAlignAllEdgesOutward) display:YES]; + [self setFrame:[_screen backingAlignedRect:windowRect + options:NSAlignAllEdgesOutward] display:YES]; // rotate the view, the core in vertical mode! if (theme.vertical) { [self.contentView setBoundsRotation:-90.0]; @@ -1312,14 +1451,24 @@ - (void)show { [self.contentView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; } [_view.textView setBoundsRotation:0.0]; - [_view.textView setBoundsOrigin:_view.textView.textContainerOrigin]; - [_view setFrame:self.contentView.bounds]; - [_view.textView setFrame:self.contentView.bounds]; + if (@available(macOS 12.0, *)) { + NSPoint textViewOrigin = _view.layoutManager.usageBoundsForTextContainer.origin; + if (_view.candidateRanges.count > 0 && _view.preeditRange.length == 0) { + textViewOrigin.y += theme.linespace / 2; + } + [_view.textView setBoundsOrigin:textViewOrigin]; + } else { + [_view.textView setBoundsOrigin:_view.textView.textContainerOrigin]; + } + NSRect boundsRect = [self backingAlignedRect:self.contentView.bounds + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + [_view setFrame:boundsRect]; + [_view.textView setFrame:boundsRect]; CGFloat translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency > 0) { - [_back setFrame:self.contentView.bounds]; + [_back setFrame:boundsRect]; [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; } else { @@ -1341,6 +1490,20 @@ - (void)hide { _maxSize = NSZeroSize; } +static CGFloat stringWidth(NSAttributedString *string, BOOL vertical){ + NSTextStorage *textStorage = [[NSTextStorage alloc] init]; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)]; + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + [layoutManager addTextContainer:textContainer]; + [textStorage addLayoutManager:layoutManager]; + [textContainer setLineFragmentPadding:0.0]; + NSTextView *textView = textContainer.textView; + [textView setLayoutOrientation:vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + [textStorage setAttributedString:string]; + [layoutManager ensureLayoutForTextContainer:textContainer]; + return NSWidth([layoutManager usedRectForTextContainer:textContainer]); +} + // Main function to add attributes to text output from librime - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange @@ -1376,7 +1539,7 @@ - (void)showPreedit:(NSString *)preedit turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; } - if (numCandidates || (preedit && preedit.length)) { + if (numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { [_statusTimer invalidate]; @@ -1395,7 +1558,7 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; [self getMaxTextWidth]; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]; // trick to force space before first line + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); CGFloat preeditWidth = 0.0; @@ -1412,7 +1575,8 @@ - (void)showPreedit:(NSString *)preedit [preeditLine appendAttributedString:[[NSAttributedString alloc] initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping attributes:theme.preeditHighlightedAttrs]]; - highlightedPreeditRange = NSMakeRange(text.length + highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); + highlightedPreeditRange = NSMakeRange(text.length + highlightedPreeditStart, + preeditLine.length - highlightedPreeditStart); } if (NSMaxRange(selRange) < preedit.length) { [preeditLine appendAttributedString:[[NSAttributedString alloc] @@ -1420,30 +1584,62 @@ - (void)showPreedit:(NSString *)preedit attributes:theme.preeditAttrs]]; } + // force caret to be rendered horizontally in vertical layout + [preeditLine addAttribute:NSVerticalGlyphFormAttributeName + value:@NO + range:NSMakeRange(caretPos, 1)]; [preeditLine addAttribute:NSParagraphStyleAttributeName value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; preeditRange = NSMakeRange(text.length, preeditLine.length); [text appendAttributedString:preeditLine]; - preeditWidth = NSWidth([preeditLine boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); - if (numCandidates) { + preeditWidth = stringWidth(preeditLine, theme.vertical); + if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } } // prepare paging and separator for width calculation but no insertion yet - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString:@"" attributes:theme.pagingAttrs]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(theme.vertical ? (pageNum ? @"▲" : @"△") : (pageNum ? @"◀" : @"◁")) - attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%lu", pageNum+1] - attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:(theme.vertical ? (lastPage ? @"▽" : @"▼") : (lastPage ? @"▷" : @"▶")) - attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]]; - - CGFloat pagingWidth = theme.showPaging ? NSWidth([paging boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; - CGFloat separatorWidth = theme.linear ? NSWidth([[[NSAttributedString alloc] initWithString:@" " attributes:theme.attrs] boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]) : 0; + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; + NSGlyphInfo *backFilled = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"◀"]; + NSGlyphInfo *backOutline = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"◁"]; + NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] + initWithString:(pageNum ? @"◀" : @"◁") + attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; + [pageUpString addAttribute:NSGlyphInfoAttributeName + value:(pageNum ? backFilled : backOutline) + range:NSMakeRange(0, pageUpString.length)]; + [paging appendAttributedString:pageUpString]; + + [paging appendAttributedString:[[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:@"%lu", pageNum+1] + attributes:theme.pagingAttrs]]; + + NSGlyphInfo *forwardOutline = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" + forFont:theme.pagingAttrs[NSFontAttributeName] baseString:@"▷"]; + NSGlyphInfo *forwardFilled = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" + forFont:theme.pagingAttrs[NSFontAttributeName] baseString:@"▶"]; + NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] + initWithString:(lastPage ? @"▷" : @"▶") + attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; + [pageDownString addAttribute:NSGlyphInfoAttributeName + value:(lastPage ? forwardOutline : forwardFilled) + range:NSMakeRange(0, pageDownString.length)]; + [paging appendAttributedString:pageDownString]; + CGFloat pagingWidth = theme.showPaging ? stringWidth(paging, theme.vertical) : 0.0; + + NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; + [sep addAttribute:NSVerticalGlyphFormAttributeName + value:@NO + range:NSMakeRange(0, sep.length)]; + CGFloat separatorWidth = theme.linear ? stringWidth(sep, theme.vertical) : 0.0; _view.seperatorWidth = separatorWidth; + CGFloat lineWidth = 0.0 - separatorWidth; CGFloat maxLineWidth = MIN(preeditWidth, _maxTextWidth); BOOL useTab = NO; @@ -1466,18 +1662,21 @@ - (void)showPreedit:(NSString *)preedit prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... - NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... - NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString attributes:labelAttrs]]; - // get the label size for indent - if (!theme.linear) { - labelWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:prefixLabelString + attributes:labelAttrs]]; + if (!theme.linear) { // get the label size for indent + labelWidth = stringWidth(line, theme.vertical); } } @@ -1486,7 +1685,9 @@ - (void)showPreedit:(NSString *)preedit [line appendAttributedString:[[NSAttributedString alloc] initWithString:candidate.precomposedStringWithCanonicalMapping attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - [line addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, line.length-candidateStart)]; + [line addAttribute:NSWritingDirectionAttributeName + value:@[@0] + range:NSMakeRange(candidateStart, line.length-candidateStart)]; if (i < comments.count && [comments[i] length] != 0) { [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " @@ -1503,20 +1704,25 @@ - (void)showPreedit:(NSString *)preedit suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... - NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... - NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } - [line appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString attributes:labelAttrs]]; + [line appendAttributedString:[[NSAttributedString alloc] + initWithString:suffixLabelString + attributes:labelAttrs]]; } NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; // determine if the line is too wide and line break is needed, based on screen size. NSString *separtatorString = @"\n"; - CGFloat candidateWidth = NSWidth([line boundingRectWithSize:NSZeroSize options:NSStringDrawingUsesLineFragmentOrigin]); + CGFloat candidateWidth = stringWidth(line, theme.vertical); + if (theme.linear) { if (i == numCandidates-1) { candidateWidth += separatorWidth + pagingWidth; @@ -1537,7 +1743,12 @@ - (void)showPreedit:(NSString *)preedit maxLineWidth = MAX(maxLineWidth, _maxSize.width); useTab = !((theme.linear ? lineWidth : pagingWidth) >= maxLineWidth); } - NSAttributedString *separator = [[NSAttributedString alloc] initWithString:separtatorString attributes:theme.attrs]; + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] + initWithString:separtatorString + attributes:theme.attrs]; + [separator addAttribute:NSVerticalGlyphFormAttributeName + value:@NO + range:NSMakeRange(0, separator.length)]; if (i > 0) { [text appendAttributedString:separator]; @@ -1551,37 +1762,51 @@ - (void)showPreedit:(NSString *)preedit // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); - if (numCandidates && theme.showPaging) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (useTab ? @"\t" : @" ") : @"\n" attributes:theme.attrs]]; + if (numCandidates > 0 && theme.showPaging) { + [text appendAttributedString:[[NSAttributedString alloc] + initWithString:theme.linear ? (useTab ? @"\t" : @" ") : @"\n" + attributes:theme.attrs]]; NSUInteger pagingStart = text.length; if (theme.linear) { [text appendAttributedString:paging]; NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; - [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType + location:maxLineWidth]]; + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePaging + range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if (useTab) { - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:paging.length-1]; - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" attributes:theme.pagingAttrs] atIndex:1]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], - [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineWidth]]; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" + attributes:theme.pagingAttrs] + atIndex:paging.length-1]; + [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" + attributes:theme.pagingAttrs] + atIndex:1]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType + location:maxLineWidth/2], + [[NSTextTab alloc] initWithType:NSRightTabStopType + location:maxLineWidth]]; } - [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; + [paging addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePaging + range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; } pagingRange = NSMakeRange(pagingStart, paging.length); } - // extra line fragment will not actually be drawn but ensures the spacing after the last line - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs]]; - - [text fixAttributesInRange:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; // text done! - [_view drawViewWith:candidateRanges highlightedIndex:index preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:turnPage]; + [_view drawViewWith:candidateRanges + highlightedIndex:index + preeditRange:preeditRange + highlightedPreeditRange:highlightedPreeditRange + pagingRange:pagingRange + pagingButton:turnPage]; [self show]; } @@ -1607,20 +1832,24 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]; - [text appendAttributedString:[[NSAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping attributes:theme.commentAttrs]]; - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; - - [text addAttribute:NSParagraphStyleAttributeName value:theme.statusParagraphStyle range:NSMakeRange(0, text.length)]; - [text fixAttributesInRange:NSMakeRange(0, text.length)]; + NSMutableAttributedString *text = [[NSMutableAttributedString alloc] + initWithString:message.precomposedStringWithCanonicalMapping + attributes:theme.commentAttrs]; + [text addAttribute:NSParagraphStyleAttributeName + value:theme.statusParagraphStyle + range:NSMakeRange(0, text.length)]; [_view.textView.textStorage setAttributedString:text]; [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; - NSRange emptyRange = NSMakeRange(NSNotFound, 0); _maxSize = NSZeroSize; // disable remember_size for status messages - [_view drawViewWith:@[] highlightedIndex:NSNotFound preeditRange:emptyRange highlightedPreeditRange:emptyRange pagingRange:emptyRange pagingButton:NSNotFound]; + NSRange emptyRange = NSMakeRange(NSNotFound, 0); + [_view drawViewWith:@[] + highlightedIndex:NSNotFound + preeditRange:emptyRange + highlightedPreeditRange:emptyRange + pagingRange:emptyRange + pagingButton:NSNotFound]; [self show]; if (_statusTimer) { @@ -1642,7 +1871,8 @@ - (void)hideStatus:(NSTimer *)timer { if (!backgroundColor) { // return foregroundColor; backgroundColor = [NSColor lightGrayColor]; } - return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:backgroundColor] + return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:backgroundColor] colorWithAlphaComponent:foregroundColor.alphaComponent]; } @@ -1667,22 +1897,32 @@ - (void)hideStatus:(NSTimer *)timer { if (validFontDescriptors.count == 0) { return nil; } + NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef) @"zh"); NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); CFRelease(systemFontRef); - NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - - NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + NSArray *fallbackDescriptors; + if (@available(macOS 12.0, *)) { + fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObject:systemFontDescriptor]; + } else { + NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; + fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + } NSDictionary *attributes = @{NSFontCascadeListAttribute:fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } -static CGFloat getLineHeight(NSFont *font) { +static CGFloat getLineHeight(NSFont *font, BOOL vertical) { + if (vertical) { + font = font.verticalFont; + } CGFloat lineHeight = font.ascender - font.descender; NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; for (NSFontDescriptor *fallback in fallbackList) { NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; + } lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); } return lineHeight; @@ -1916,9 +2156,9 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:labelFontSize]; - CGFloat fontHeight = getLineHeight(font); - CGFloat labelFontHeight = getLineHeight(labelFont); - CGFloat commentFontHeight = getLineHeight(commentFont); + CGFloat fontHeight = getLineHeight(font, vertical); + CGFloat labelFontHeight = getLineHeight(labelFont, vertical); + CGFloat commentFontHeight = getLineHeight(commentFont, vertical); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; @@ -1970,11 +2210,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.1)); - pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset - (linear ? 0.0 : labelFontSize * 0.1)); - - preeditAttrs[NSLigatureAttributeName] = @0; - preeditHighlightedAttrs[NSLigatureAttributeName] = @0; + pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; NSColor *accentColor = [[self class] accentColor]; @@ -2004,16 +2241,10 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingAttrs[NSForegroundColorAttributeName] = linear ? candidateLabelColor : candidateTextColor; pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedCandidateTextColor; - attrs[NSVerticalGlyphFormAttributeName] = @(vertical); labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - highlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - commentAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - commentHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - preeditAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - preeditHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - pagingAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); + pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(NO); [theme setStatusMessageType:statusMessageType]; From 494c696d1e055ebbf86e5581ebab8b29a18e72cf Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 29 Jun 2023 20:51:12 +0200 Subject: [PATCH 103/164] Update .gitmodules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 26ab0c14c..1ebd84b73 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ [submodule "Sparkle"] path = Sparkle url = https://github.com/sparkle-project/Sparkle - branch = 2.4.1 + branch = 2.x From 6e37c381b9d192ef6aa57269e93bc6dd206545a0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 29 Jun 2023 20:55:54 +0200 Subject: [PATCH 104/164] Update Sparkle --- Sparkle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sparkle b/Sparkle index ec547530e..8fb9c83ad 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit ec547530e9e50181574a1eae7dc9c021800415d8 +Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 From f57b50e180b239d752b4e0ca8eada9bffd407455 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 29 Jun 2023 21:26:18 +0200 Subject: [PATCH 105/164] no caret with swtichers --- SquirrelInputController.m | 2 +- SquirrelPanel.m | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index f3b570181..112cf0e4b 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -644,7 +644,7 @@ -(void)rimeUpdate } [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) selRange:selRange - caretPos:caretPos + caretPos:switcher ? NSNotFound : caretPos candidates:candidates comments:comments labels:labels diff --git a/SquirrelPanel.m b/SquirrelPanel.m index b43d7a15d..e7ef88af6 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1583,11 +1583,12 @@ - (void)showPreedit:(NSString *)preedit initWithString:[preedit substringFromIndex:NSMaxRange(selRange)].precomposedStringWithCanonicalMapping attributes:theme.preeditAttrs]]; } - // force caret to be rendered horizontally in vertical layout - [preeditLine addAttribute:NSVerticalGlyphFormAttributeName - value:@NO - range:NSMakeRange(caretPos, 1)]; + if (caretPos != NSNotFound) { + [preeditLine addAttribute:NSVerticalGlyphFormAttributeName + value:@NO + range:NSMakeRange(caretPos, 1)]; + } [preeditLine addAttribute:NSParagraphStyleAttributeName value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; From f80ecc89ce197e1805044952a461e399d3b92b8d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 30 Jun 2023 02:22:39 +0200 Subject: [PATCH 106/164] background Image --- SquirrelPanel.m | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index e7ef88af6..d8a282f39 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -926,17 +926,24 @@ - (void)drawRect:(NSRect)dirtyRect { borderPath.windingRule = NSEvenOddWindingRule; } - // set layers] + // set layers _shape.path = [backgroundPath quartzPath]; _shape.fillColor = [[NSColor whiteColor] CGColor]; + _shape.cornerRadius = theme.cornerRadius; CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; textContainerLayer.path = [textContainerPath quartzPath]; textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; - [self.layer setSublayers: NULL]; - CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; + textContainerLayer.cornerRadius = theme.hilitedCornerRadius; + [self.layer setSublayers:NULL]; + self.layer.cornerRadius = theme.cornerRadius; if (theme.backgroundImage) { - panelLayer.backgroundColor = [theme.backgroundImage CGColor]; + CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; + backgroundLayer.path = [backgroundPath quartzPath]; + backgroundLayer.fillColor = [theme.backgroundImage CGColor]; + backgroundLayer.cornerRadius = theme.cornerRadius; + [self.layer addSublayer:backgroundLayer]; } + CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; panelLayer.path = [textContainerPath quartzPath]; panelLayer.fillColor = [theme.backgroundColor CGColor]; [self.layer addSublayer:panelLayer]; @@ -1832,6 +1839,7 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message } - (void)showStatus:(NSString *)message { + [self getMaxTextWidth]; SquirrelTheme *theme = _view.currentTheme; NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message.precomposedStringWithCanonicalMapping From a415608da8b4a06785cc974c06f13ad487d1c7a6 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 3 Jul 2023 08:47:35 +0200 Subject: [PATCH 107/164] vertical centering --- SquirrelPanel.m | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d8a282f39..2aac0826c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1910,13 +1910,8 @@ - (void)hideStatus:(NSTimer *)timer { CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef) @"zh"); NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); CFRelease(systemFontRef); - NSArray *fallbackDescriptors; - if (@available(macOS 12.0, *)) { - fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObject:systemFontDescriptor]; - } else { - NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; - } + NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; + NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; NSDictionary *attributes = @{NSFontCascadeListAttribute:fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } @@ -1926,15 +1921,19 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { font = font.verticalFont; } CGFloat lineHeight = font.ascender - font.descender; - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; - for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; - if (vertical) { - fallbackFont = fallbackFont.verticalFont; + if (@available(macOS 12.0, *)) { + return lineHeight; + } else { + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; + } + lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); } - lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); + return lineHeight; } - return lineHeight; } static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfig *config, NSString *prefix) { @@ -2184,6 +2183,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; + pagingParagraphStyle.paragraphSpacingBefore = pagingFont.leading; NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; statusParagraphStyle.minimumLineHeight = commentFontHeight; From d4e2031bc1390ac306d9c27fce59a3315a48cbec Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 3 Jul 2023 16:59:57 +0200 Subject: [PATCH 108/164] fixed line_length --- SquirrelPanel.m | 37 +++++++++++++++++++++++++++++-------- data/squirrel.yaml | 2 ++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 2aac0826c..32b26521c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -70,6 +70,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) CGFloat translucency; +@property(nonatomic, readonly) CGFloat lineLength; @property(nonatomic, readonly) BOOL showPaging; @property(nonatomic, readonly) BOOL rememberSize; @property(nonatomic, readonly) BOOL linear; @@ -113,6 +114,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(CGFloat)translucency + lineLength:(CGFloat)lineLength showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize linear:(BOOL)linear @@ -195,6 +197,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(CGFloat)translucency + lineLength:(CGFloat)lineLength showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize linear:(BOOL)linear @@ -208,6 +211,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _preeditLinespace = preeditLinespace; _alpha = alpha; _translucency = translucency; + _lineLength = lineLength; _showPaging = showPaging; _rememberSize = rememberSize; _linear = linear; @@ -321,7 +325,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { if (@available(macOS 12.0, *)) { _layoutManager = [[NSTextLayoutManager alloc] init]; _layoutManager.usesFontLeading = NO; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; _layoutManager.textContainer = textContainer; NSTextContentStorage *textStorage = [[NSTextContentStorage alloc] init]; [textStorage addTextLayoutManager:_layoutManager]; @@ -339,7 +343,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _textView.layoutManager.backgroundLayoutEnabled = YES; _textView.layoutManager.usesFontLeading = NO; _textView.layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; [_textView replaceTextContainer:textContainer]; } _defaultTheme = [[SquirrelTheme alloc] init]; @@ -1342,6 +1346,9 @@ - (void)getMaxTextWidth { NSRect screenRect = [_screen visibleFrame]; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); _maxTextWidth = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2; + if (theme.lineLength > 0) { + _maxTextWidth = MIN(theme.lineLength, _maxTextWidth); + } } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect @@ -1371,11 +1378,16 @@ - (void)show { bool sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; NSRect maxContentRect = NSInsetRect(contentRect, theme.hilitedCornerRadius, 0); - // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if (theme.rememberSize) { - if (theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) : - (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : - (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(screenRect)))) { + if (theme.lineLength > 0) { // fixed line length / text width + if (_maxSize.width > 0) { // only applicable to non-status + maxContentRect.size.width = _maxTextWidth; + } + } + if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) + if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) : + (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : + (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(screenRect)))) && + theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { @@ -1564,6 +1576,9 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; [self getMaxTextWidth]; + if (theme.lineLength > 0) { + _maxSize.width = MIN(theme.lineLength, _maxTextWidth); + } NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); @@ -1851,7 +1866,7 @@ - (void)showStatus:(NSString *)message { [_view.textView.textStorage setAttributedString:text]; [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; - _maxSize = NSZeroSize; // disable remember_size for status messages + _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages NSRange emptyRange = NSMakeRange(NSNotFound, 0); [_view drawViewWith:@[] highlightedIndex:NSNotFound @@ -1997,6 +2012,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; CGFloat spacing = [config getDouble:@"style/spacing"]; CGFloat baseOffset = [config getDouble:@"style/base_offset"]; + CGFloat lineLength = MAX([config getDouble:@"style/line_length"], 0.0); NSColor *backgroundColor; NSColor *backgroundImage; @@ -2147,6 +2163,10 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f if (baseOffsetOverridden) { baseOffset = baseOffsetOverridden.doubleValue; } + NSNumber *lineLengthOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_length"]]; + if (lineLengthOverridden) { + lineLength = MAX(lineLengthOverridden.doubleValue, 0.0); + } } fontSize = fontSize ? fontSize : kDefaultFontSize; @@ -2289,6 +2309,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f preeditLinespace:spacing alpha:(alpha == 0 ? 1.0 : alpha) translucency:translucency + lineLength:lineLength showPaging:showPaging rememberSize:rememberSize linear:linear diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 66d3f0104..3168c6469 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -39,6 +39,8 @@ style: line_spacing: 5 # space between preedit and candidates in non-inline mode spacing: 10 + # line length of preedit and candidates (fixed if value > 0, flexible/auto-adjust otherwise) + line_length: 0 #candidate_format: '%c %@' From 244fe91af0cd9285ad1052f218210012e7d9fcbb Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 4 Jul 2023 05:56:53 +0200 Subject: [PATCH 109/164] update layer --- SquirrelPanel.m | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 32b26521c..129e63ef0 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -272,6 +272,7 @@ @interface SquirrelView : NSView @property(nonatomic, assign) CGFloat seperatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; @property(nonatomic, getter=isFlipped, readonly) BOOL flipped; +@property(nonatomic, readonly) BOOL wantsUpdateLayer; - (void) drawViewWith:(NSArray *)candidateRanges highlightedIndex:(NSUInteger)highlightedIndex @@ -297,6 +298,10 @@ - (BOOL)isFlipped { return YES; } +- (BOOL)wantsUpdateLayer { + return YES; +} + - (BOOL)isDark { if (@available(macOS 10.14, *)) { if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { @@ -320,6 +325,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.masksToBounds = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; + self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; } if (@available(macOS 12.0, *)) { @@ -728,7 +734,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } // All draws happen here -- (void)drawRect:(NSRect)dirtyRect { +- (void)updateLayer { NSBezierPath *backgroundPath; NSBezierPath *borderPath; NSBezierPath *textContainerPath; @@ -741,7 +747,7 @@ - (void)drawRect:(NSRect)dirtyRect { SquirrelTheme *theme = self.currentTheme; NSPoint lineOrigin = NSZeroPoint; - NSRect backgroundRect = dirtyRect; + NSRect backgroundRect = self.bounds; NSRect textContainerRect = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); NSRect preeditRect = NSZeroRect; NSRect candidateBlockRect = NSZeroRect; @@ -1395,12 +1401,13 @@ - (void)show { [textContainer setSize:NSMakeSize(_maxSize.width + theme.hilitedCornerRadius * 2, maxTextHeight)]; } } - if (theme.vertical ? (NSMinX(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : - (NSMinY(_position) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? 0 : kOffsetHeight))) { + if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : + (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? 0 : kOffsetHeight))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { maxContentRect.size.height = _maxSize.height; + [textContainer setSize:NSMakeSize(_maxTextWidth + theme.hilitedCornerRadius * 2, _maxSize.height)]; } } } From 0ce7184517d53e9aad01764e00fd7eb2b5a19947 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 4 Jul 2023 22:29:57 +0200 Subject: [PATCH 110/164] inverse color to match dark/light appearance --- SquirrelPanel.m | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 129e63ef0..754587a5f 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1102,7 +1102,7 @@ - (BOOL)rememberSize { } + (NSColor *)secondaryTextColor { - if(@available(macOS 10.10, *)) { + if (@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; } else { return [NSColor disabledControlTextColor]; @@ -1262,9 +1262,9 @@ - (void)sendEvent:(NSEvent *)event { if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index || cursorIndex == _turnPage) { [self.inputController perform:kSELECT onIndex:cursorIndex]; - _mouseDown = NO; } } + _mouseDown = NO; } break; case NSEventTypeRightMouseUp: { NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; @@ -1272,9 +1272,9 @@ - (void)sendEvent:(NSEvent *)event { if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { [self.inputController perform:kDELETE onIndex:cursorIndex]; - _mouseDown = NO; } } + _mouseDown = NO; } break; case NSEventTypeMouseEntered: { self.acceptsMouseMovedEvents = YES; @@ -1907,6 +1907,18 @@ - (void)hideStatus:(NSTimer *)timer { colorWithAlphaComponent:foregroundColor.alphaComponent]; } +static inline NSColor *inverseColor(NSColor *color) { + if (color == nil) { + return nil; + } else { + return [NSColor colorWithColorSpace:color.colorSpace + hue:color.hueComponent + saturation:color.saturationComponent + brightness:1-color.brightnessComponent + alpha:color.alphaComponent]; + } +} + static NSFontDescriptor *getFontDescriptor(NSString *fullname) { if (fullname == nil) { return nil; @@ -2252,6 +2264,26 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSColor *secondaryTextColor = [[self class] secondaryTextColor]; NSColor *accentColor = [[self class] accentColor]; + if (@available(macOS 10.14, *)) { + if (theme.translucency > 0 && + ((backgroundColor.brightnessComponent >= 0.5 && isDark) || + (backgroundColor.brightnessComponent < 0.5 && !isDark))) { + backgroundColor = inverseColor(backgroundColor); + borderColor = inverseColor(borderColor) ; + preeditBackgroundColor = inverseColor(preeditBackgroundColor); + candidateTextColor = inverseColor(candidateTextColor); + highlightedCandidateTextColor = [inverseColor(highlightedCandidateTextColor) highlightWithLevel:highlightedCandidateTextColor.brightnessComponent]; + highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1-highlightedCandidateBackColor.brightnessComponent]; + candidateLabelColor = inverseColor(candidateLabelColor); + highlightedCandidateLabelColor = [inverseColor(highlightedCandidateLabelColor) highlightWithLevel:highlightedCandidateLabelColor.brightnessComponent]; + commentTextColor = inverseColor(commentTextColor); + highlightedCommentTextColor = [inverseColor(highlightedCommentTextColor) highlightWithLevel:highlightedCommentTextColor.brightnessComponent]; + textColor = inverseColor(textColor); + highlightedTextColor = [inverseColor(highlightedTextColor) highlightWithLevel:highlightedTextColor.brightnessComponent]; + highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1-highlightedBackColor.brightnessComponent]; + } + } + backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; borderColor = borderColor ? borderColor : isNative ? [NSColor gridColor] : nil; preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : isNative ? [NSColor windowBackgroundColor] : nil; From aed426922c3ed7535a0952b3934eec5b3cc53783 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 8 Jul 2023 05:50:59 +0200 Subject: [PATCH 111/164] update packages --- Sparkle | 2 +- librime | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sparkle b/Sparkle index 8fb9c83ad..5018f4a9e 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 +Subproject commit 5018f4a9e2dfa289662635b33dbc1a1b3468470f diff --git a/librime b/librime index c63099eb5..a10a92ceb 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit c63099eb5e8372e5b2968830784daf791a996060 +Subproject commit a10a92ceba7969a2582219131e6f9fbcbd0baf7e From a01be5050895cd5298ff3069f97631f15ade056c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 9 Jul 2023 04:44:23 +0200 Subject: [PATCH 112/164] fix gap between highlight and panel border --- SquirrelApplicationDelegate.m | 2 +- SquirrelPanel.m | 102 +++++++++++++++++----------------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 81cbe9ffc..10c1688e2 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -90,7 +90,7 @@ void notification_handler(void* context_object, RimeSessionId session_id, const char* option_name = message_value + !state; struct rime_string_slice_t state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); struct rime_string_slice_t state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); - + if (state_label_long.str || state_label_short.str) { const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; show_status_message(state_label_long.str, short_message, message_type); diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 754587a5f..0155e0020 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -281,7 +281,6 @@ - (void) drawViewWith:(NSArray *)candidateRanges pagingRange:(NSRange)pagingRange pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; -- (NSRect)contentRectForTextRange:(NSTextRange *)range API_AVAILABLE(macos(12.0)); - (NSRect)setLineRectForRange:(NSRange)charRange atOrigin:(NSPoint)origin withReferenceFont:(NSFont *)refFont @@ -477,20 +476,15 @@ - (NSRect)contentRect { return contentRect; } -// Get the rectangle containing the range of text, will first convert to glyph range, expensive to calculate -- (NSRect)contentRectForTextRange:(NSTextRange *)range API_AVAILABLE(macos(12.0)) { - __block NSRect contentRect = NSZeroRect; - [self.layoutManager enumerateTextSegmentsInRange:range type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { - contentRect = NSUnionRect(contentRect, segmentRect); - return YES; - }]; - return contentRect; -} - +// Get the rectangle containing the range of text, will first convert to glyph or text range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:range]; - NSRect contentRect = [self contentRectForTextRange:textRange]; + __block NSRect contentRect = NSZeroRect; + [self.layoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + contentRect = NSUnionRect(contentRect, segmentRect); + return YES; + }]; return contentRect; } else { NSRange glyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; @@ -592,7 +586,7 @@ BOOL nearEmptyRect(NSRect rect) { } // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle -// bodyRect is complete lines in the middle +// bodyRect is the complete line fragment in the middle if the range spans no less than one full line - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { NSSize edgeInset = self.currentTheme.edgeInset; *leadingRect = NSZeroRect; @@ -635,15 +629,15 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSLayoutManager *layoutManager = self.textView.layoutManager; NSTextContainer *textContainer = self.textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSPoint startPoint = [layoutManager locationForGlyphAtIndex:glyphRange.location]; - NSPoint endPoint = [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)]; + CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; + CGFloat endX = NSMaxX([layoutManager boundingRectForGlyphRange:NSMakeRange(NSMaxRange(glyphRange)-1, 1) inTextContainer:textContainer]); NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; - if (NSMaxRange(leadingLineRange) > NSMaxRange(glyphRange)) { - *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { + *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, - endPoint.x - startPoint.x, NSHeight(leadingLineRect)); + endX - startX, NSHeight(leadingLineRect)); } else { CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - self.currentTheme.hilitedCornerRadius, NSMaxX(boundingRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); @@ -654,22 +648,22 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, - NSMinX(trailingLineRect) + endPoint.x - leftEdge, NSHeight(trailingLineRect)); + NSMinX(trailingLineRect) + endX - leftEdge, NSHeight(trailingLineRect)); if (glyphRange.location == leadingLineRange.location) { *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startPoint.x + edgeInset.width, + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, - rightEdge - NSMinX(leadingLineRect) - startPoint.x, NSHeight(leadingLineRect)); + rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); if (bodyLineRange.length > 0) { *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, @@ -765,10 +759,6 @@ - (void)updateLayer { candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); candidateBlockRect.origin = lineOrigin; lineOrigin.y = NSMaxY(candidateBlockRect); - } else if (_preeditRange.length == 0) { // status message - candidateBlockRect = [self contentRectForTextRange:self.layoutManager.textContentManager.documentRange]; - candidateBlockRect.origin = lineOrigin; - lineOrigin.y = NSMaxY(candidateBlockRect); } if (!theme.linear && _pagingRange.length > 0) { pagingLineRect = [self contentRectForRange:_pagingRange]; @@ -833,16 +823,35 @@ - (void)updateLayer { NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, -theme.linespace/2); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, -theme.linespace/2); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, -theme.linespace/2); + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, 0); if (@available(macOS 12.0, *)) { if (_preeditRange.length == 0) { - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSOffsetRect(leadingRect, 0.0, theme.linespace/2); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSOffsetRect(bodyRect, 0.0, theme.linespace/2); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSOffsetRect(trailingRect, 0.0, theme.linespace/2); + leadingRect.origin.y += theme.linespace/2; + bodyRect.origin.y += theme.linespace/2; + trailingRect.origin.y += theme.linespace/2; } } + if (!NSIsEmptyRect(leadingRect)) { + leadingRect.origin.y -= theme.linespace/2; + leadingRect.size.height += theme.linespace/2; + leadingRect = [self backingAlignedRect:leadingRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + } + if (!NSIsEmptyRect(trailingRect)) { + trailingRect.size.height += theme.linespace/2; + trailingRect = [self backingAlignedRect:trailingRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + } + if (!NSIsEmptyRect(bodyRect)) { + if (NSIsEmptyRect(leadingRect)) { + bodyRect.origin.y -= theme.linespace/2; + bodyRect.size.height += theme.linespace/2; + } + if (NSIsEmptyRect(trailingRect)) { + bodyRect.size.height += theme.linespace/2; + } + bodyRect = [self backingAlignedRect:bodyRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + } NSMutableArray *candidatePoints; NSMutableArray *candidatePoints2; // Handles the special case where containing boxes are separated @@ -870,6 +879,7 @@ - (void)updateLayer { candidateRect = NSOffsetRect(candidateRect, 0.0, theme.linespace/2); } } + candidateRect = [self backingAlignedRect:candidateRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; @@ -885,9 +895,9 @@ - (void)updateLayer { NSRect bodyRect; NSRect trailingRect; [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSIntersectionRect(leadingRect, innerBox); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSIntersectionRect(bodyRect, innerBox); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSIntersectionRect(trailingRect, innerBox); + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(leadingRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(bodyRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(trailingRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated @@ -921,6 +931,8 @@ - (void)updateLayer { pageUpRect = NSOffsetRect(pageUpRect, 0.0, theme.linespace/2); } } + pageDownRect = [self backingAlignedRect:pageDownRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + pageUpRect = [self backingAlignedRect:pageUpRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*NSHeight(pageUpRect), 0.28*NSWidth(pageUpRect)); _pagingPaths[0] = pageUpPath; @@ -1093,14 +1105,6 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -- (BOOL)showPaging { - return _view.currentTheme.showPaging; -} - -- (BOOL)rememberSize { - return _view.currentTheme.rememberSize; -} - + (NSColor *)secondaryTextColor { if (@available(macOS 10.10, *)) { return [NSColor secondaryLabelColor]; @@ -1647,7 +1651,7 @@ - (void)showPreedit:(NSString *)preedit [paging appendAttributedString:pageUpString]; [paging appendAttributedString:[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@"%lu", pageNum+1] + initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] attributes:theme.pagingAttrs]]; NSGlyphInfo *forwardOutline = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" @@ -1808,12 +1812,8 @@ - (void)showPreedit:(NSString *)preedit } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if (useTab) { - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" - attributes:theme.pagingAttrs] - atIndex:paging.length-1]; - [paging insertAttributedString:[[NSAttributedString alloc] initWithString:@"\t" - attributes:theme.pagingAttrs] - atIndex:1]; + [paging replaceCharactersInRange:NSMakeRange(1, 1) withString:@"\t"]; + [paging replaceCharactersInRange:NSMakeRange(paging.length-2, 1) withString:@"\t"]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineWidth/2], [[NSTextTab alloc] initWithType:NSRightTabStopType From 523edbdb4b3c3b5672b58a1dba881b31fbe72a33 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 9 Jul 2023 10:47:27 +0200 Subject: [PATCH 113/164] revert originalString composedString --- SquirrelInputController.m | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 112cf0e4b..9821a8136 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -345,8 +345,13 @@ -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { - NSString *composition = [self composedString:sender]; - [self commitString:composition]; + if ([_preeditString isEqualToString:@" "]) { + const char* raw_input = rime_get_api()->get_input(_session); + if (raw_input) + [self commitString:@(raw_input)]; + } else { // inline mode + [self commitString:_preeditString]; + } rime_get_api()->clear_composition(_session); } } @@ -385,30 +390,6 @@ -(NSMenu*)menu return NSApp.squirrelAppDelegate.menu; } -- (NSAttributedString *)originalString:(id)sender -{ - const char* raw_input = rime_get_api()->get_input(_session); - NSAttributedString *originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; - return originalString; -} - -- (id)composedString:(id)sender -{ - RIME_STRUCT(RimeContext, ctx); - const char *preedit = ctx.composition.preedit; - if (!preedit) - return @""; - if (rime_get_api()->get_option(_session, "soft_cursor")) { - int caretPos = ctx.composition.cursor_pos; - char composed[strlen(preedit)-3]; - for (int i = 0; i < strlen(preedit)-3; ++i) { - composed[i] = preedit[i < caretPos ? i : i+3]; - } - return @(composed); - } - return @(preedit); -} - -(NSArray*)candidates:(id)sender { return _candidates; From edf34bc8c78eba65202415afe75348ab4fe60e47 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 9 Jul 2023 13:51:52 +0200 Subject: [PATCH 114/164] boost 1.78 --- INSTALL.md | 6 +++--- autobuild.sh | 2 +- librime | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index a2c55e83d..c31f03eec 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as @@ -115,10 +115,10 @@ To build only for the native architecture, pass variable `ARCHS` to `make`: ``` sh # for Mac computers with Apple Silicon -make ARCHS='arm64' +make ARCHS='arm64' MACOSX_DEPLOYMENT_TARGET='10.13' # for Intel-based Mac -make ARCHS='x86_64' +make ARCHS='x86_64' MACOSX_DEPLOYMENT_TARGET='10.13' ``` ## Install it on your Mac diff --git a/autobuild.sh b/autobuild.sh index fc6e18594..5804b05d4 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -8,7 +8,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_81_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" export BUILD_UNIVERSAL=1 diff --git a/librime b/librime index a10a92ceb..7c2179983 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit a10a92ceba7969a2582219131e6f9fbcbd0baf7e +Subproject commit 7c217998363384e6e6b9d44ebe89dc0a2412a7ed From c2d23650732a751b8ea8e3f58e730c2676d61497 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 10 Jul 2023 04:54:00 +0200 Subject: [PATCH 115/164] Update SquirrelPanel.m --- SquirrelPanel.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 0155e0020..6b80225ad 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -984,13 +984,6 @@ - (void)updateLayer { [panelLayer addSublayer:candidateLayer]; } } - if (theme.borderColor && ![borderPath isEmpty]) { - CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; - borderLayer.path = [borderPath quartzPath]; - borderLayer.fillColor = [theme.borderColor CGColor]; - borderLayer.fillRule = kCAFillRuleEvenOdd; - [panelLayer addSublayer:borderLayer]; - } CIFilter *backColorFilter = [CIFilter filterWithName:@"CISourceATopCompositing"]; panelLayer.compositingFilter = backColorFilter; if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { @@ -1037,6 +1030,13 @@ - (void)updateLayer { [self.layer addSublayer:highlightedPreeditLayer]; } } + if (theme.borderColor && ![borderPath isEmpty]) { + CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; + borderLayer.path = [borderPath quartzPath]; + borderLayer.fillColor = [theme.borderColor CGColor]; + borderLayer.fillRule = kCAFillRuleEvenOdd; + [panelLayer addSublayer:borderLayer]; + } [self.textView setTextContainerInset:theme.edgeInset]; } From 6041bbdd52560facb110a902928e78eadbf30386 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 13 Jul 2023 12:00:47 +0200 Subject: [PATCH 116/164] originalString composedString --- SquirrelInputController.m | 42 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 9821a8136..08c79057f 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -20,7 +20,8 @@ -(void)updateAppOptions; @implementation SquirrelInputController { id _currentClient; NSString *_preeditString; - NSString *_inlineString; + NSAttributedString *_originalString; + NSString *_composedString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; @@ -345,13 +346,8 @@ -(void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { - if ([_preeditString isEqualToString:@" "]) { - const char* raw_input = rime_get_api()->get_input(_session); - if (raw_input) - [self commitString:@(raw_input)]; - } else { // inline mode - [self commitString:_preeditString]; - } + NSString *composition = [self composedString:sender]; + [self commitString:composition]; rime_get_api()->clear_composition(_session); } } @@ -390,6 +386,16 @@ -(NSMenu*)menu return NSApp.squirrelAppDelegate.menu; } +-(NSAttributedString *)originalString:(id)sender +{ + return _originalString; +} + +-(id)composedString:(id)sender +{ + return _composedString; +} + -(NSArray*)candidates:(id)sender { return _candidates; @@ -407,6 +413,8 @@ -(void)commitString:(NSString*)string replacementRange:NSMakeRange(NSNotFound, 0)]; _preeditString = @""; + _composedString = @""; + _originalString = [[NSAttributedString alloc] initWithString:@""]; [NSApp.squirrelAppDelegate.panel hide]; } @@ -566,6 +574,24 @@ -(void)rimeUpdate const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; + // update composed string + if (!preedit || switcher) { + _composedString = @""; + } else if (rime_get_api()->get_option(_session, "soft_cursor")) { + int caretPos = ctx.composition.cursor_pos; + char composed[strlen(preedit)-3]; + for (int i = 0; i < strlen(preedit)-3; ++i) { + composed[i] = preedit[i < caretPos ? i : i+3]; + } + _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; + } else { + _composedString = [@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]; + } + + // update raw input + const char *raw_input = rime_get_api()->get_input(_session); + _originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; + NSUInteger start = substr(preedit, ctx.composition.sel_start).length; NSUInteger end = substr(preedit, ctx.composition.sel_end).length; NSUInteger caretPos = substr(preedit, ctx.composition.cursor_pos).length; From a72e58dfaed62e13093379c713321c82f16778d7 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 13 Jul 2023 15:49:11 +0200 Subject: [PATCH 117/164] cleanup --- SquirrelPanel.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 6b80225ad..ff15a299c 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1220,7 +1220,6 @@ - (instancetype)init { // ^ May fix visibility issue in fullscreen games. self.level = CGShieldingWindowLevel(); self.hasShadow = YES; - self.opaque = NO; self.backgroundColor = [NSColor clearColor]; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.frame]; @@ -1246,6 +1245,10 @@ - (instancetype)init { return self; } +- (BOOL)isOpaque { + return NO; +} + - (void)sendEvent:(NSEvent *)event { switch (event.type) { case NSEventTypeLeftMouseDown: @@ -1385,7 +1388,7 @@ - (void)show { CGFloat maxTextHeight = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - theme.edgeInset.height * 2; [textContainer setSize:NSMakeSize(_maxTextWidth + theme.hilitedCornerRadius * 2, maxTextHeight)]; - bool sweepVertical = NSWidth(_position) > NSHeight(_position); + BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; NSRect maxContentRect = NSInsetRect(contentRect, theme.hilitedCornerRadius, 0); if (theme.lineLength > 0) { // fixed line length / text width From 02520e2a2eaf433e662a3b120e9fce1e74d83836 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 15 Jul 2023 01:59:35 +0200 Subject: [PATCH 118/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 7c2179983..0360726d0 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 7c217998363384e6e6b9d44ebe89dc0a2412a7ed +Subproject commit 0360726d016fb0c1696a2e25b2a44730dc7ae171 From c0e28c612889815f56242593c0071bf0a121d747 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 24 Jul 2023 04:35:00 +0200 Subject: [PATCH 119/164] macOS 14 compatible --- SquirrelPanel.m | 1420 +++++++++++++++++++++++++---------------------- 1 file changed, 755 insertions(+), 665 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index ff15a299c..86c6e1395 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -6,43 +6,47 @@ @implementation NSBezierPath (BezierPathQuartzUtilities) // This method works only in OS X v10.2 and later. - (CGPathRef)quartzPath { - // Need to begin a path here. - CGPathRef immutablePath = NULL; - // Then draw the path elements. - NSUInteger numElements = self.elementCount; - if (numElements > 0) { - CGMutablePathRef path = CGPathCreateMutable(); - NSPoint points[3]; - BOOL didClosePath = YES; - for (NSUInteger i = 0; i < numElements; i++) { - switch ([self elementAtIndex:i associatedPoints:points]) { - case NSMoveToBezierPathElement: - CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); - break; - case NSLineToBezierPathElement: - CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); - didClosePath = NO; - break; - case NSCurveToBezierPathElement: - CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, - points[1].x, points[1].y, - points[2].x, points[2].y); - didClosePath = NO; - break; - case NSClosePathBezierPathElement: - CGPathCloseSubpath(path); - didClosePath = YES; - break; + if (@available(macOS 14.0, *)) { + return self.CGPath; + } else { + // Need to begin a path here. + CGPathRef immutablePath = NULL; + // Then draw the path elements. + NSUInteger numElements = self.elementCount; + if (numElements > 0) { + CGMutablePathRef path = CGPathCreateMutable(); + NSPoint points[3]; + BOOL didClosePath = YES; + for (NSUInteger i = 0; i < numElements; i++) { + switch ([self elementAtIndex:i associatedPoints:points]) { + case NSMoveToBezierPathElement: + CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); + break; + case NSLineToBezierPathElement: + CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); + didClosePath = NO; + break; + case NSCurveToBezierPathElement: + CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, + points[1].x, points[1].y, + points[2].x, points[2].y); + didClosePath = NO; + break; + case NSClosePathBezierPathElement: + CGPathCloseSubpath(path); + didClosePath = YES; + break; + } } + // Be sure the path is closed or Quartz may not do valid hit detection. + if (!didClosePath) { + CGPathCloseSubpath(path); + } + immutablePath = CFAutorelease(CGPathCreateCopy(path)); + CGPathRelease(path); } - // Be sure the path is closed or Quartz may not do valid hit detection. - if (!didClosePath) { - CGPathCloseSubpath(path); - } - immutablePath = CFAutorelease(CGPathCreateCopy(path)); - CGPathRelease(path); + return immutablePath; } - return immutablePath; } @end @@ -122,21 +126,21 @@ - (void)setCornerRadius:(CGFloat)cornerRadius inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate; -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs -commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs -preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs; - -- (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle - pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle - statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; +- (void) setAttrs:(NSMutableDictionary *)attrs + highlightedAttrs:(NSMutableDictionary *)highlightedAttrs + labelAttrs:(NSMutableDictionary *)labelAttrs + labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs + commentAttrs:(NSMutableDictionary *)commentAttrs + commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs + preeditAttrs:(NSMutableDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSMutableDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs; + +- (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle + statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; @end @@ -169,7 +173,7 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { } - (void)setStatusMessageType:(NSString *)type { - if ([type isEqualToString: @"long"] || [type isEqualToString: @"short"] || [type isEqualToString: @"mix"]) { + if ([type isEqualToString:@"long"] || [type isEqualToString:@"short"] || [type isEqualToString:@"mix"]) { _statusMessageType = type; } else { _statusMessageType = @"mix"; @@ -229,7 +233,7 @@ - (void) setAttrs:(NSMutableDictionary *)attrs preeditAttrs:(NSMutableDictionary *)preeditAttrs preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs{ + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs { _attrs = attrs; _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; @@ -242,10 +246,10 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _pagingHighlightedAttrs = pagingHighlightedAttrs; } -- (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle - preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle - pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle - statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle{ +- (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle + preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle + pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle + statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle { _paragraphStyle = paragraphStyle; _preeditParagraphStyle = preeditParagraphStyle; _pagingParagraphStyle = pagingParagraphStyle; @@ -257,6 +261,7 @@ - (void) setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView @property(nonatomic, readonly) NSTextView *textView; +@property(nonatomic, readonly) NSEdgeInsets insets; @property(nonatomic, readonly) NSArray *candidateRanges; @property(nonatomic, readonly) NSUInteger highlightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @@ -269,22 +274,24 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, readonly) NSTextLayoutManager *layoutManager API_AVAILABLE(macos(12.0)); @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; -@property(nonatomic, assign) CGFloat seperatorWidth; +@property(nonatomic, assign) CGFloat separatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; -@property(nonatomic, getter=isFlipped, readonly) BOOL flipped; +@property(nonatomic, getter = isFlipped, readonly) BOOL flipped; @property(nonatomic, readonly) BOOL wantsUpdateLayer; -- (void) drawViewWith:(NSArray *)candidateRanges - highlightedIndex:(NSUInteger)highlightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton; +- (void) drawViewWithInsets:(NSEdgeInsets)insets + candidateRanges:(NSArray *)candidateRanges + highlightedIndex:(NSUInteger)highlightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange + pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; - (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPoint)origin + atOrigin:(NSPoint *)origin withReferenceFont:(NSFont *)refFont paragraphStyle:(NSParagraphStyle *)style; + @end @implementation SquirrelView @@ -322,35 +329,39 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self = [super initWithFrame:frameRect]; if (self) { self.wantsLayer = YES; + self.layer.geometryFlipped = YES; self.layer.masksToBounds = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; - self.layerContentsPlacement = NSViewLayerContentsPlacementBottomLeft; } if (@available(macOS 12.0, *)) { _layoutManager = [[NSTextLayoutManager alloc] init]; _layoutManager.usesFontLeading = NO; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + NSTextContainer *textContainer = [[NSTextContainer alloc] + initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; _layoutManager.textContainer = textContainer; NSTextContentStorage *textStorage = [[NSTextContentStorage alloc] init]; [textStorage addTextLayoutManager:_layoutManager]; - _textView = [[NSTextView alloc] initWithFrame:frameRect textContainer:_layoutManager.textContainer]; - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - _textView.wantsLayer = NO; + _textView = [[NSTextView alloc] initWithFrame:frameRect + textContainer:_layoutManager.textContainer]; } else { - _textView = [[NSTextView alloc] initWithFrame:frameRect]; - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - _textView.wantsLayer = NO; - _textView.layoutManager.backgroundLayoutEnabled = YES; - _textView.layoutManager.usesFontLeading = NO; - _textView.layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; - [_textView replaceTextContainer:textContainer]; - } + NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + layoutManager.backgroundLayoutEnabled = YES; + layoutManager.usesFontLeading = NO; + layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; + NSTextContainer *textContainer = [[NSTextContainer alloc] + initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + [layoutManager addTextContainer:textContainer]; + NSTextStorage *textStorage = [[NSTextStorage alloc] init]; + [textStorage addLayoutManager:layoutManager]; + _textView = [[NSTextView alloc] initWithFrame:frameRect + textContainer:textContainer]; + } + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + _textView.wantsLayer = NO; + _defaultTheme = [[SquirrelTheme alloc] init]; _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { @@ -363,7 +374,7 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) if (range.location == NSNotFound) { return nil; } else { - NSTextContentManager *contentManager = self.layoutManager.textContentManager; + NSTextContentManager *contentManager = _layoutManager.textContentManager; id startLocation = [contentManager locationFromLocation:contentManager.documentRange.location withOffset:range.location]; id endLocation = [contentManager locationFromLocation:startLocation withOffset:range.length]; return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; @@ -371,21 +382,21 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) } - (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPoint)origin + atOrigin:(NSPoint *)origin withReferenceFont:(NSFont *)refFont paragraphStyle:(NSParagraphStyle *)style { - NSLayoutManager *layoutManager = self.textView.layoutManager; - NSTextContainer *textContainer = self.textView.textContainer; - NSTextStorage *textStorage = self.textView.textStorage; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; + NSTextStorage *textStorage = _textView.textStorage; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSRect blockRect = NSMakeRect(origin.x, origin.y, 0, 0); BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; CGFloat refFontHeight = refFont.ascender - refFont.descender; CGFloat refBaseline = refFont.ascender; CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + NSRect blockRect = NSZeroRect; NSUInteger i = glyphRange.location; NSRange lineRange = NSMakeRange(i, 0); while (i < NSMaxRange(glyphRange)) { @@ -393,30 +404,29 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; - rect.origin.y = NSMaxY(blockRect); - usedRect.origin.y = NSMaxY(blockRect); - CGFloat alignment = verticalLayout ? lineHeight/2 : refBaseline + MAX(0.0, lineHeight - refFontHeight)/2; + rect.origin.y = origin->y; + usedRect.origin.y = origin->y; rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); + CGFloat alignment = verticalLayout ? lineHeight / 2 : refBaseline + MAX(0.0, lineHeight - refFontHeight) / 2; if (style.lineSpacing > 0) { rect.size.height = lineHeight + style.lineSpacing; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight + style.lineSpacing); - alignment += style.lineSpacing/2; + alignment += style.lineSpacing / 2; } - if (style.paragraphSpacing > 0 && [textStorage.string characterAtIndex:NSMaxRange(lineCharRange)-1] == '\n') { + if (style.paragraphSpacing > 0 && NSMaxRange(lineCharRange) != textStorage.length && + [textStorage.string characterAtIndex:NSMaxRange(lineCharRange) - 1] == '\n') { rect.size.height += style.paragraphSpacing; } - if (style.paragraphSpacingBefore > 0 && (lineCharRange.location == 0 || - [textStorage.string characterAtIndex:lineCharRange.location-1] == '\n')) { + if (style.paragraphSpacingBefore > 0 && lineCharRange.location != 0 && + [textStorage.string characterAtIndex:lineCharRange.location - 1] == '\n') { rect.size.height += style.paragraphSpacingBefore; usedRect.origin.y += style.paragraphSpacingBefore; alignment += style.paragraphSpacingBefore; } + [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:NSIntersectionRect(usedRect, rect)]; blockRect = NSUnionRect(blockRect, rect); - usedRect = NSIntersectionRect(usedRect, rect); - rect = [self.textView backingAlignedRect:rect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - usedRect = [self.textView backingAlignedRect:usedRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:usedRect]; + origin->y = NSMaxY(rect); // typesetting glyphs NSRange fontRunRange = NSMakeRange(NSNotFound, 0); @@ -427,7 +437,8 @@ - (NSRect)setLineRectForRange:(NSRange)charRange NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; CGFloat baselineOffset = [[textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL] doubleValue]; - NSRange runRange = NSIntersectionRange(fontRunRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + NSRange fontRunGlyphRange = [layoutManager characterRangeForGlyphRange:fontRunRange actualGlyphRange:NULL]; + NSRange runRange = NSIntersectionRange(fontRunGlyphRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { runFont = runFont.verticalFont; resizedRefFont = resizedRefFont.verticalFont; @@ -436,17 +447,16 @@ - (NSRect)setLineRectForRange:(NSRange)charRange CGFloat runFontHeight = runFont.ascender - runFont.descender; CGFloat resizedRefFontHeight = resizedRefFont.ascender - resizedRefFont.descender; if (verticalLayout) { - runGlyphPosition.y = alignment - baselineOffset + MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + runGlyphPosition.y = alignment - baselineOffset + ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/4); if (runFont.isVertical) { - runGlyphPosition.x += MAX(0.0, runFontHeight - resizedRefFontHeight)/2; + runGlyphPosition.x += ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/2); } else { runGlyphPosition.y += runBaseline - runFontHeight/2; } } else { runGlyphPosition.y = alignment - baselineOffset; } - NSRect lineDrawnRect = [self.textView backingAlignedRect:NSMakeRect(rect.origin.x, rect.origin.y, runGlyphPosition.x, runGlyphPosition.y) options:(NSAlignAllEdgesInward|NSAlignRectFlipped)]; - [layoutManager setLocation:NSMakePoint(NSWidth(lineDrawnRect), NSHeight(lineDrawnRect)) forStartOfGlyphRange:runRange]; + [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } i = NSMaxRange(lineRange); @@ -456,24 +466,14 @@ - (NSRect)setLineRectForRange:(NSRange)charRange // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { - NSRect contentRect; if (@available(macOS 12.0, *)) { - [self.layoutManager ensureLayoutForRange:self.layoutManager.textContentManager.documentRange]; - contentRect = [self.layoutManager usageBoundsForTextContainer]; + [_layoutManager ensureLayoutForRange:_layoutManager.textContentManager.documentRange]; + return NSInsetRect([_layoutManager usageBoundsForTextContainer], + -_textView.textContainer.lineFragmentPadding, 0); } else { - [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; - contentRect = [self.textView.layoutManager usedRectForTextContainer:self.textView.textContainer]; + [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; + return [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; } - if (_candidateRanges.count > 0) { - if (_preeditRange.length == 0) { - contentRect.origin.y -= self.currentTheme.linespace/2; - contentRect.size.height += self.currentTheme.linespace/2; - } - if (self.currentTheme.linear || _pagingRange.length == 0) { - contentRect.size.height += self.currentTheme.linespace/2; - } - } - return contentRect; } // Get the rectangle containing the range of text, will first convert to glyph or text range, expensive to calculate @@ -481,28 +481,39 @@ - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:range]; __block NSRect contentRect = NSZeroRect; - [self.layoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + [_layoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: + ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { contentRect = NSUnionRect(contentRect, segmentRect); return YES; }]; return contentRect; } else { - NSRange glyphRange = [self.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect rect = [self.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textView.textContainer]; - NSRect firstLineRect = [self.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:NULL]; - NSRect finalLineRect = [self.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:NULL]; - NSRect contentRect = NSMakeRect(NSMinX(rect), NSMinY(firstLineRect), NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range + actualCharacterRange:NULL]; + NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange + inTextContainer:_textView.textContainer]; + NSRect firstLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location + effectiveRange:NULL]; + NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 + effectiveRange:NULL]; + NSRect contentRect = NSMakeRect(NSMinX(rect), NSMinY(firstLineRect), + NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); return contentRect; } } // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWith:(NSArray*)candidateRanges - highlightedIndex:(NSUInteger)highlightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton { +- (void) drawViewWithInsets:(NSEdgeInsets)insets + candidateRanges:(NSArray *)candidateRanges + highlightedIndex:(NSUInteger)highlightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange + pagingButton:(NSUInteger)pagingButton { + _insets = insets; _candidateRanges = candidateRanges; _highlightedIndex = highlightedIndex; _preeditRange = preeditRange; @@ -514,67 +525,52 @@ - (void) drawViewWith:(NSArray*)candidateRanges self.needsDisplay = YES; } -// A tweaked sign function, to winddown corner radius when the size is small -double sign(double number) { - if (number >= 2) { - return 1; - } else if (number <= -2) { - return -1; - } else { - return number / 2; - } -} - // Bezier cubic curve, which has continuous roundness -NSBezierPath *drawSmoothLines(NSArray *vertex, CGFloat alpha, CGFloat beta) { +NSBezierPath * drawRoundedPolygon(NSArray *vertex, CGFloat radius) { NSBezierPath *path = [NSBezierPath bezierPath]; - if (vertex.count < 1) + if (vertex.count < 1) { return path; - NSPoint previousPoint = vertex[vertex.count-1].pointValue; - NSPoint point = vertex[0].pointValue; + } + NSPoint previousPoint = vertex.lastObject.pointValue; + NSPoint point = vertex.firstObject.pointValue; NSPoint nextPoint; - NSPoint control1; - NSPoint control2; - NSPoint target = previousPoint; + NSPoint startPoint; + NSPoint endPoint = previousPoint; + CGFloat arcRadius; CGVector diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); if (ABS(diff.dx) >= ABS(diff.dy)) { - target.x += sign(diff.dx/beta)*beta; + endPoint.x += diff.dx / 2; + endPoint.y = point.y; } else { - target.y += sign(diff.dy/beta)*beta; + endPoint.y += diff.dy / 2; + endPoint.x = point.x; } - [path moveToPoint:target]; + [path moveToPoint:endPoint]; for (NSUInteger i = 0; i < vertex.count; ++i) { - previousPoint = vertex[(vertex.count+i-1)%vertex.count].pointValue; + startPoint = endPoint; point = vertex[i].pointValue; - nextPoint = vertex[(i+1)%vertex.count].pointValue; - target = point; - control1 = point; - diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.dx) >= ABS(diff.dy)) { - target.x -= sign(diff.dx/beta)*beta; - control1.x -= sign(diff.dx/beta)*alpha; - } else { - target.y -= sign(diff.dy/beta)*beta; - control1.y -= sign(diff.dy/beta)*alpha; - } - [path lineToPoint:target]; - target = point; - control2 = point; + nextPoint = vertex[(i + 1) % vertex.count].pointValue; + arcRadius = MIN(radius, MAX(ABS(point.x - startPoint.x), ABS(point.y - startPoint.y))); + endPoint = point; diff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); if (ABS(diff.dx) > ABS(diff.dy)) { - control2.x += sign(diff.dx/beta)*alpha; - target.x += sign(diff.dx/beta)*beta; + endPoint.x += diff.dx / 2; + arcRadius = MIN(arcRadius, ABS(diff.dx) / 2); + endPoint.y = nextPoint.y; + point.y = nextPoint.y; } else { - control2.y += sign(diff.dy/beta)*alpha; - target.y += sign(diff.dy/beta)*beta; + endPoint.y += diff.dy / 2; + arcRadius = MIN(arcRadius, ABS(diff.dy) / 2); + endPoint.x = nextPoint.x; + point.x = nextPoint.x; } - [path curveToPoint:target controlPoint1:control1 controlPoint2:control2]; + [path appendBezierPathWithArcFromPoint:point toPoint:endPoint radius:arcRadius]; } [path closePath]; return path; } -NSArray *rectVertex(NSRect rect) { +NSArray * rectVertex(NSRect rect) { return @[@(rect.origin), @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), @@ -588,16 +584,16 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is the complete line fragment in the middle if the range spans no less than one full line - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { - NSSize edgeInset = self.currentTheme.edgeInset; - *leadingRect = NSZeroRect; - *bodyRect = NSZeroRect; - *trailingRect = NSZeroRect; if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:charRange]; NSMutableArray *lineRects = [[NSMutableArray alloc] init]; - [self.layoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + [_layoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: + ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { if (!nearEmptyRect(segmentRect)) { - [lineRects addObject:[NSValue valueWithRect:NSOffsetRect(segmentRect, edgeInset.width, edgeInset.height)]]; + [lineRects addObject:[NSValue valueWithRect:segmentRect]]; } return YES; }]; @@ -616,57 +612,60 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else { *trailingRect = lastLineRect; if (NSMinX(firstLineRect) == NSMinX(lastLineRect)) { - *bodyRect = NSUnionRect(firstLineRect, lineRects[lineRects.count-2].rectValue); + *bodyRect = NSUnionRect(firstLineRect, lineRects[lineRects.count - 2].rectValue); } else { *leadingRect = firstLineRect; if (lineRects.count > 2) { - *bodyRect = NSUnionRect(lineRects[1].rectValue, lineRects[lineRects.count-2].rectValue); + *bodyRect = NSUnionRect(lineRects[1].rectValue, lineRects[lineRects.count - 2].rectValue); } } } } } else { - NSLayoutManager *layoutManager = self.textView.layoutManager; - NSTextContainer *textContainer = self.textView.textContainer; - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSTextContainer *textContainer = _textView.textContainer; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange + actualCharacterRange:NULL]; CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; - CGFloat endX = NSMaxX([layoutManager boundingRectForGlyphRange:NSMakeRange(NSMaxRange(glyphRange)-1, 1) inTextContainer:textContainer]); - NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer]; + CGFloat endX = NSMaxX([layoutManager boundingRectForGlyphRange:NSMakeRange(NSMaxRange(glyphRange) - 1, 1) + inTextContainer:textContainer]); + NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange + inTextContainer:textContainer]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange withoutAdditionalLayout:YES]; + NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location + effectiveRange:&leadingLineRange]; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, + *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startX, + NSMinY(leadingLineRect), endX - startX, NSHeight(leadingLineRect)); } else { - CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - self.currentTheme.hilitedCornerRadius, NSMaxX(boundingRect)); + CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - textContainer.lineFragmentPadding, NSMaxX(boundingRect)); NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 effectiveRange:&trailingLineRange withoutAdditionalLayout:YES]; - CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + self.currentTheme.hilitedCornerRadius, NSMinX(boundingRect)); + NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 + effectiveRange:&trailingLineRange]; + CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + textContainer.lineFragmentPadding, NSMinX(boundingRect)); if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX, NSMinY(leadingLineRect), rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { - *trailingRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(trailingLineRect) + edgeInset.height, + *trailingRect = NSMakeRect(leftEdge, NSMinY(trailingLineRect), NSMinX(trailingLineRect) + endX - leftEdge, NSHeight(trailingLineRect)); if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMinY(leadingLineRect) + edgeInset.height, + *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX + edgeInset.width, - NSMinY(leadingLineRect) + edgeInset.height, + *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX, + NSMinY(leadingLineRect), rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); - NSRange bodyLineRange = NSMakeRange(NSMaxRange(leadingLineRange), trailingLineRange.location-NSMaxRange(leadingLineRange)); - if (bodyLineRange.length > 0) { - *bodyRect = NSMakeRect(leftEdge + edgeInset.width, NSMaxY(leadingLineRect) + edgeInset.height, + if (trailingLineRange.location > NSMaxRange(leadingLineRange)) { + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } @@ -684,28 +683,31 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } else if (nearEmptyRect(leadingRect) && nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { return rectVertex(bodyRect); } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { - NSArray * leadingVertex = rectVertex(leadingRect); - NSArray * bodyVertex = rectVertex(bodyRect); + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *bodyVertex = rectVertex(bodyRect); return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3]]; } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { - NSArray * trailingVertex = rectVertex(trailingRect); - NSArray * bodyVertex = rectVertex(bodyRect); + NSArray *trailingVertex = rectVertex(trailingRect); + NSArray *bodyVertex = rectVertex(bodyRect); return @[bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3]]; - } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && nearEmptyRect(bodyRect) && NSMaxX(leadingRect)>NSMinX(trailingRect)) { - NSArray * leadingVertex = rectVertex(leadingRect); - NSArray * trailingVertex = rectVertex(trailingRect); - return @[leadingVertex[0], leadingVertex[1], trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3]]; + } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && + nearEmptyRect(bodyRect) && NSMaxX(leadingRect) > NSMinX(trailingRect)) { + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *trailingVertex = rectVertex(trailingRect); + return @[leadingVertex[0], leadingVertex[1], trailingVertex[0], trailingVertex[1], + trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3]]; } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { - NSArray * leadingVertex = rectVertex(leadingRect); - NSArray * bodyVertex = rectVertex(bodyRect); - NSArray * trailingVertex = rectVertex(trailingRect); - return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3]]; + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *bodyVertex = rectVertex(bodyRect); + NSArray *trailingVertex = rectVertex(trailingRect); + return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], trailingVertex[1], + trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3]]; } else { return @[]; } } -NSColor *hooverColor(NSColor *color, BOOL darkTheme) { +NSColor * hooverColor(NSColor *color, BOOL darkTheme) { if (@available(macOS 10.14, *)) { return [color colorWithSystemEffect:NSColorSystemEffectRollover]; } @@ -716,7 +718,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe } } -NSColor *disabledColor(NSColor *color, BOOL darkTheme) { +NSColor * disabledColor(NSColor *color, BOOL darkTheme) { if (@available(macOS 10.14, *)) { return [color colorWithSystemEffect:NSColorSystemEffectDisabled]; } @@ -739,209 +741,235 @@ - (void)updateLayer { NSBezierPath *pageDownPath; SquirrelTheme *theme = self.currentTheme; - NSPoint lineOrigin = NSZeroPoint; - NSRect backgroundRect = self.bounds; NSRect textContainerRect = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); + CGFloat linePadding = _textView.textContainer.lineFragmentPadding; + + NSRange visibleRange; + if (@available(macOS 12.0, *)) { + visibleRange = NSMakeRange(0, _textView.textContentStorage.textStorage.length); + } else { + NSRange containerGlyphRange = {NSNotFound, 0}; + [_textView.layoutManager textContainerForGlyphAtIndex:0 effectiveRange:&containerGlyphRange]; + visibleRange = [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange actualGlyphRange:NULL]; + } + + NSRange preeditRange = NSIntersectionRange(_preeditRange, visibleRange); + NSRange candidateBlockRange = NSIntersectionRange(NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue), visibleRange); + NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange); + NSRect preeditRect = NSZeroRect; NSRect candidateBlockRect = NSZeroRect; NSRect pagingLineRect = NSZeroRect; - + NSRect statusRect = NSZeroRect; if (@available(macOS 12.0, *)) { - if (_preeditRange.length > 0) { - preeditRect = [self contentRectForRange:_preeditRange]; - preeditRect.size.height += theme.preeditLinespace; - preeditRect.origin = lineOrigin; - lineOrigin.y = NSMaxY(preeditRect); - } - if (_candidateRanges.count > 0) { - NSRange candidateBlockRange = NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue); - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); - candidateBlockRect.origin = lineOrigin; - lineOrigin.y = NSMaxY(candidateBlockRect); - } - if (!theme.linear && _pagingRange.length > 0) { - pagingLineRect = [self contentRectForRange:_pagingRange]; - pagingLineRect.origin = lineOrigin; - lineOrigin.y = NSMaxY(pagingLineRect); - } - } else { - // perform typesetting to get vertically centered layout and get the block rect - if (_preeditRange.length > 0) { - NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); - preeditRect = [self setLineRectForRange:_preeditRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) paragraphStyle:theme.preeditParagraphStyle]; - lineOrigin.y = NSMaxY(preeditRect); + if (preeditRange.length > 0) { + preeditRect = [self contentRectForRange:preeditRange]; + if (candidateBlockRange.length > 0) { + preeditRect.size.height += theme.preeditLinespace; + } } - if (_candidateRanges.count > 0) { - CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], MAX([theme.commentAttrs[NSFontAttributeName] pointSize], [theme.labelAttrs[NSFontAttributeName] pointSize])); - NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef) @"zh")); - NSRange candidateBlockRange = NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue); - candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:lineOrigin withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; - lineOrigin.y = NSMaxY(candidateBlockRect); - } else if (_preeditRange.length == 0) { // status message - NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef) @"zh")); - candidateBlockRect = [self setLineRectForRange:NSMakeRange(0, self.textView.textStorage.length) atOrigin:lineOrigin withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) paragraphStyle:theme.statusParagraphStyle]; - lineOrigin.y = NSMaxY(candidateBlockRect); + if (candidateBlockRange.length > 0) { + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace / 2); + if (preeditRange.length == 0) { + candidateBlockRect.origin.y += theme.linespace / 2; + } + } else if (preeditRange.length == 0) { // status message + statusRect = [self contentRectForRange:visibleRange]; } - if (!theme.linear && _pagingRange.length > 0) { - pagingLineRect = [self setLineRectForRange:_pagingRange atOrigin:lineOrigin withReferenceFont:theme.pagingAttrs[NSFontAttributeName] paragraphStyle:theme.pagingParagraphStyle]; - lineOrigin.y = NSMaxY(pagingLineRect); + if (!theme.linear && pagingRange.length > 0) { + pagingLineRect = [self contentRectForRange:pagingRange]; + pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; + pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; } - } - if (_candidateRanges.count > 0) { - if (_preeditRange.length == 0) { - candidateBlockRect.origin.y -= theme.linespace/2; - candidateBlockRect.size.height += theme.linespace/2; + } else { + NSPoint lineOrigin = NSZeroPoint; + if (preeditRange.length > 0) { + NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef)@"zh")); + preeditRect = [self setLineRectForRange:preeditRange + atOrigin:&lineOrigin + withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) + paragraphStyle:theme.preeditParagraphStyle]; + } + if (candidateBlockRange.length > 0) { + CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], + MAX([theme.commentAttrs[NSFontAttributeName] pointSize], + [theme.labelAttrs[NSFontAttributeName] pointSize])); + NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef)@"zh")); + candidateBlockRect = [self setLineRectForRange:candidateBlockRange + atOrigin:&lineOrigin + withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) + paragraphStyle:theme.paragraphStyle]; + if (preeditRange.length == 0) { + candidateBlockRect.size.height += theme.linespace / 2; + } + if (theme.linear || pagingRange.length == 0) { + candidateBlockRect.size.height += theme.linespace / 2; + } + } else if (preeditRange.length == 0) { // status message + NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef)@"zh")); + statusRect = [self setLineRectForRange:visibleRange + atOrigin:&lineOrigin + withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) + paragraphStyle:theme.statusParagraphStyle]; } - if (theme.linear || _pagingRange.length == 0) { - candidateBlockRect.size.height += theme.linespace/2; + if (!theme.linear && pagingRange.length > 0) { + pagingLineRect = [self setLineRectForRange:pagingRange + atOrigin:&lineOrigin + withReferenceFont:theme.pagingAttrs[NSFontAttributeName] + paragraphStyle:theme.pagingParagraphStyle]; } } [NSBezierPath setDefaultLineWidth:0]; // Draw preedit Rect - if (_preeditRange.length > 0) { + if (preeditRange.length > 0) { preeditRect.size.width = textContainerRect.size.width; preeditRect.origin = textContainerRect.origin; + // Draw highlighted part of preedit text + NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); + if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { + NSRect innerBox = NSInsetRect(preeditRect, linePadding, 0); + if (candidateBlockRange.length > 0) { + innerBox.size.height -= theme.preeditLinespace; + } + NSRect leadingRect = NSZeroRect; + NSRect bodyRect = NSZeroRect; + NSRect trailingRect = NSZeroRect; + [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect + : NSIntersectionRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect + : NSIntersectionRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect + : NSIntersectionRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + NSMutableArray *highlightedPreeditPoints; + NSMutableArray *highlightedPreeditPoints2; + // Handles the special case where containing boxes are separated + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) + && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; + highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; + } else { + highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + } + highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight/3)); + if (highlightedPreeditPoints2.count > 0) { + [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight/3))]; + } + } } // Draw candidate Rect - if (_candidateRanges.count > 0) { + if (candidateBlockRange.length > 0) { candidateBlockRect.size.width = textContainerRect.size.width; candidateBlockRect.origin.x = textContainerRect.origin.x; - candidateBlockRect.origin.y += theme.edgeInset.height; - if (theme.preeditBackgroundColor != nil) { - candidateBlockPath = drawSmoothLines(rectVertex(candidateBlockRect), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - } + candidateBlockRect.origin.y += textContainerRect.origin.y; + candidateBlockRect = NSIntersectionRect(candidateBlockRect, textContainerRect); + candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.hilitedCornerRadius); // Draw candidate highlight rect - if (theme.linear) { - CGFloat highlightPadding = MIN(_seperatorWidth/2, theme.hilitedCornerRadius); - for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = _candidateRanges[i].rangeValue; - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { + NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + if (candidateRange.length == 0) { + break; + } + if (theme.linear) { + NSRect leadingRect = NSZeroRect; + NSRect bodyRect = NSZeroRect; + NSRect trailingRect = NSZeroRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -highlightPadding, 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -highlightPadding, 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -highlightPadding, 0); - if (@available(macOS 12.0, *)) { - if (_preeditRange.length == 0) { - leadingRect.origin.y += theme.linespace/2; - bodyRect.origin.y += theme.linespace/2; - trailingRect.origin.y += theme.linespace/2; - } + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + if (preeditRange.length == 0) { + leadingRect.origin.y += theme.linespace / 2; + bodyRect.origin.y += theme.linespace / 2; + trailingRect.origin.y += theme.linespace / 2; } if (!NSIsEmptyRect(leadingRect)) { - leadingRect.origin.y -= theme.linespace/2; - leadingRect.size.height += theme.linespace/2; - leadingRect = [self backingAlignedRect:leadingRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + leadingRect.origin.y -= theme.linespace / 2; + leadingRect.size.height += theme.linespace / 2; + leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); } if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.height += theme.linespace/2; - trailingRect = [self backingAlignedRect:trailingRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + trailingRect.size.height += theme.linespace / 2; + trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); } if (!NSIsEmptyRect(bodyRect)) { if (NSIsEmptyRect(leadingRect)) { - bodyRect.origin.y -= theme.linespace/2; - bodyRect.size.height += theme.linespace/2; + bodyRect.origin.y -= theme.linespace / 2; + bodyRect.size.height += theme.linespace / 2; } if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.height += theme.linespace/2; + bodyRect.size.height += theme.linespace / 2; } - bodyRect = [self backingAlignedRect:bodyRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); } NSMutableArray *candidatePoints; NSMutableArray *candidatePoints2; // Handles the special case where containing boxes are separated - if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && + !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { candidatePoints = [rectVertex(leadingRect) mutableCopy]; candidatePoints2 = [rectVertex(trailingRect) mutableCopy]; } else { candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; } - NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); if (candidatePoints2.count > 0) { - [candidatePath appendBezierPath:drawSmoothLines(candidatePoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; + [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; } _candidatePaths[i] = candidatePath; - } - } else { - for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = _candidateRanges[i].rangeValue; - NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace/2); + } else { + NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); candidateRect.size.width = textContainerRect.size.width; candidateRect.origin.x = textContainerRect.origin.x; - candidateRect.origin.y += theme.edgeInset.height; - if (@available(macOS 12.0, *)) { - if (_preeditRange.length == 0) { - candidateRect = NSOffsetRect(candidateRect, 0.0, theme.linespace/2); - } + candidateRect.origin.y += textContainerRect.origin.y; + if (preeditRange.length == 0) { + candidateRect.origin.y += theme.linespace / 2; } - candidateRect = [self backingAlignedRect:candidateRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; + candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; - NSBezierPath *candidatePath = drawSmoothLines(candidatePoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; } } } - // Draw highlighted part of preedit text - if (_highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect innerBox = NSInsetRect(preeditRect, theme.hilitedCornerRadius, 0); - innerBox.size.height -= theme.preeditLinespace; - NSRect leadingRect; - NSRect bodyRect; - NSRect trailingRect; - [self multilineRectForRange:_highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(leadingRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(bodyRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(trailingRect, innerBox) options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - NSMutableArray *highlightedPreeditPoints; - NSMutableArray *highlightedPreeditPoints2; - // Handles the special case where containing boxes are separated - if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; - } else { - highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; - } - highlightedPreeditPath = drawSmoothLines(highlightedPreeditPoints, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); - if (highlightedPreeditPoints2.count > 0) { - [highlightedPreeditPath appendBezierPath:drawSmoothLines(highlightedPreeditPoints2, 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius)]; - } - } - // Draw paging Rect - if (_pagingRange.length > 0) { - CGFloat buttonPadding = theme.linear ? MIN(_seperatorWidth/2, theme.hilitedCornerRadius) : theme.hilitedCornerRadius; - NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(_pagingRange)-1, 1)], theme.edgeInset.width, theme.edgeInset.height); + if (pagingRange.length > 0) { + CGFloat buttonPadding = theme.linear ? MIN(_separatorWidth / 2, linePadding) : linePadding; + NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)], + textContainerRect.origin.x, textContainerRect.origin.y); pageDownRect.size.width += buttonPadding; - NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(_pagingRange.location, 1)], theme.edgeInset.width, theme.edgeInset.height); + NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 1)], + textContainerRect.origin.x, textContainerRect.origin.y); pageUpRect.origin.x -= buttonPadding; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented if (theme.linear) { - pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace/2); - pageUpRect = NSInsetRect(pageUpRect, 0.0, -theme.linespace/2); - } - if (@available(macOS 12.0, *)) { - if (_preeditRange.length == 0) { - pageDownRect = NSOffsetRect(pageDownRect, 0.0, theme.linespace/2); - pageUpRect = NSOffsetRect(pageUpRect, 0.0, theme.linespace/2); - } - } - pageDownRect = [self backingAlignedRect:pageDownRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - pageUpRect = [self backingAlignedRect:pageUpRect options:(NSAlignAllEdgesOutward|NSAlignRectFlipped)]; - pageDownPath = drawSmoothLines(rectVertex(pageDownRect), 0.06*NSHeight(pageDownRect), 0.28*NSWidth(pageDownRect)); - pageUpPath = drawSmoothLines(rectVertex(pageUpRect), 0.06*NSHeight(pageUpRect), 0.28*NSWidth(pageUpRect)); + pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace / 2); + pageUpRect = NSInsetRect(pageUpRect, 0.0, -theme.linespace / 2); + } + if (preeditRange.length == 0) { + pageDownRect = NSOffsetRect(pageDownRect, 0.0, theme.linespace / 2); + pageUpRect = NSOffsetRect(pageUpRect, 0.0, theme.linespace / 2); + } + pageDownRect = NSIntersectionRect(pageDownRect, textContainerRect); + pageUpRect = NSIntersectionRect(pageUpRect, textContainerRect); + pageDownPath = drawRoundedPolygon(rectVertex(pageDownRect), + MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect))/3)); + pageUpPath = drawRoundedPolygon(rectVertex(pageUpRect), + MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect))/3)); _pagingPaths[0] = pageUpPath; _pagingPaths[1] = pageDownPath; } // Draw borders - backgroundPath = drawSmoothLines(rectVertex(backgroundRect), 0.3*theme.cornerRadius, 1.4*theme.cornerRadius); - textContainerPath = drawSmoothLines(rectVertex(textContainerRect), 0.3*theme.hilitedCornerRadius, 1.4*theme.hilitedCornerRadius); + backgroundPath = drawRoundedPolygon(rectVertex(backgroundRect), + MIN(theme.cornerRadius, NSHeight(backgroundRect)/3)); + textContainerPath = drawRoundedPolygon(rectVertex(textContainerRect), + MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3)); if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { borderPath = [backgroundPath copy]; [borderPath appendBezierPath:textContainerPath]; @@ -951,18 +979,18 @@ - (void)updateLayer { // set layers _shape.path = [backgroundPath quartzPath]; _shape.fillColor = [[NSColor whiteColor] CGColor]; - _shape.cornerRadius = theme.cornerRadius; + _shape.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; textContainerLayer.path = [textContainerPath quartzPath]; textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; - textContainerLayer.cornerRadius = theme.hilitedCornerRadius; + textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3); [self.layer setSublayers:NULL]; - self.layer.cornerRadius = theme.cornerRadius; + self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); if (theme.backgroundImage) { CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; backgroundLayer.path = [backgroundPath quartzPath]; backgroundLayer.fillColor = [theme.backgroundImage CGColor]; - backgroundLayer.cornerRadius = theme.cornerRadius; + backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); [self.layer addSublayer:backgroundLayer]; } CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; @@ -975,7 +1003,7 @@ - (void)updateLayer { } } if (theme.preeditBackgroundColor && - (_preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { + (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { panelLayer.fillColor = [theme.preeditBackgroundColor CGColor]; if (![candidateBlockPath isEmpty]) { CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; @@ -986,7 +1014,7 @@ - (void)updateLayer { } CIFilter *backColorFilter = [CIFilter filterWithName:@"CISourceATopCompositing"]; panelLayer.compositingFilter = backColorFilter; - if (_highlightedIndex != NSNotFound && theme.highlightedStripColor) { + if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; highlightedPath = _candidatePaths[_highlightedIndex]; highlightedLayer.path = [highlightedPath quartzPath]; @@ -998,7 +1026,7 @@ - (void)updateLayer { [self.layer addSublayer:highlightedLayer]; } NSColor *buttonColor = theme.linear ? theme.highlightedStripColor : theme.highlightedPreeditColor; - if (_pagingRange.length > 0 && buttonColor) { + if (pagingRange.length > 0 && buttonColor) { CAShapeLayer *pagingLayer = [[CAShapeLayer alloc] init]; switch (_pagingButton) { case NSPageUpFunctionKey: { @@ -1022,7 +1050,7 @@ - (void)updateLayer { [self.layer addSublayer:pagingLayer]; } if (theme.highlightedPreeditColor) { - if(![highlightedPreeditPath isEmpty]) { + if (![highlightedPreeditPath isEmpty]) { CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; @@ -1037,7 +1065,8 @@ - (void)updateLayer { borderLayer.fillRule = kCAFillRuleEvenOdd; [panelLayer addSublayer:borderLayer]; } - [self.textView setTextContainerInset:theme.edgeInset]; + + [_textView display]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { @@ -1084,11 +1113,15 @@ @implementation SquirrelPanel { BOOL _lastPage; BOOL _mouseDown; NSPoint _scrollLocus; - + NSString *_statusMessage; NSTimer *_statusTimer; } +- (BOOL)isFloatingPanel { + return YES; +} + - (BOOL)linear { return _view.currentTheme.linear; } @@ -1128,9 +1161,13 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSColor *secondaryTextColor = [[self class] secondaryTextColor]; NSColor *accentColor = [[self class] accentColor]; - NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; - NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; - NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:kDefaultFontSize]; + NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) + size:kDefaultFontSize]; + NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) + size:kDefaultFontSize]; + NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] + fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] + size:kDefaultFontSize]; NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; @@ -1172,7 +1209,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingAttrs[NSFontAttributeName] = symbolFont; NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; pagingHighlightedAttrs[NSFontAttributeName] = symbolFont; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; @@ -1180,7 +1217,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *statusParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - preeditParagraphStyle.lineBreakMode = NSLineBreakByCharWrapping; + preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; preeditParagraphStyle.alignment = NSTextAlignmentLeft; paragraphStyle.alignment = NSTextAlignmentLeft; pagingParagraphStyle.alignment = NSTextAlignmentLeft; @@ -1193,16 +1230,16 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs - labelAttrs:labelAttrs - labelHighlightedAttrs:labelHighlightedAttrs - commentAttrs:commentAttrs - commentHighlightedAttrs:commentHighlightedAttrs - preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs]; + [theme setAttrs:attrs + highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs + labelHighlightedAttrs:labelHighlightedAttrs + commentAttrs:commentAttrs + commentHighlightedAttrs:commentHighlightedAttrs + preeditAttrs:preeditAttrs + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle pagingParagraphStyle:pagingParagraphStyle @@ -1211,18 +1248,21 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:(NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless) + styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; + if (self) { self.alphaValue = 1.0; // _window.level = NSScreenSaverWindowLevel + 1; // ^ May fix visibility issue in fullscreen games. self.level = CGShieldingWindowLevel(); - self.hasShadow = YES; + self.hasShadow = NO; + self.opaque = NO; + self.displaysWhenScreenProfileChanges = YES; self.backgroundColor = [NSColor clearColor]; NSView *contentView = [[NSView alloc] init]; - _view = [[SquirrelView alloc] initWithFrame:self.contentView.frame]; + _view = [[SquirrelView alloc] initWithFrame:self.contentView.bounds]; if (@available(macOS 10.14, *)) { _back = [[NSVisualEffectView alloc] init]; _back.blendingMode = NSVisualEffectBlendingModeBehindWindow; @@ -1245,15 +1285,12 @@ - (instancetype)init { return self; } -- (BOOL)isOpaque { - return NO; -} - - (void)sendEvent:(NSEvent *)event { switch (event.type) { case NSEventTypeLeftMouseDown: case NSEventTypeRightMouseDown: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || @@ -1264,21 +1301,23 @@ - (void)sendEvent:(NSEvent *)event { } } break; case NSEventTypeLeftMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index || cursorIndex == _turnPage) { - [self.inputController perform:kSELECT onIndex:cursorIndex]; + [_inputController perform:kSELECT onIndex:cursorIndex]; } } _mouseDown = NO; } break; case NSEventTypeRightMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; NSUInteger cursorIndex = NSNotFound; if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { - [self.inputController perform:kDELETE onIndex:cursorIndex]; + [_inputController perform:kDELETE onIndex:cursorIndex]; } } _mouseDown = NO; @@ -1290,11 +1329,12 @@ - (void)sendEvent:(NSEvent *)event { self.acceptsMouseMovedEvents = NO; } break; case NSEventTypeMouseMoved: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; NSUInteger cursorIndex = NSNotFound; if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { - [self.inputController perform:kCHOOSE onIndex:cursorIndex]; + [_inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { @@ -1321,16 +1361,16 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + [_inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { - [self.inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; + [_inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [self.inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + [_inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { - [self.inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; + [_inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } } @@ -1358,9 +1398,9 @@ - (void)getMaxTextWidth { [self getCurrentScreen]; NSRect screenRect = [_screen visibleFrame]; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _maxTextWidth = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2; + _maxTextWidth = floor((theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2); if (theme.lineLength > 0) { - _maxTextWidth = MIN(theme.lineLength, _maxTextWidth); + _maxTextWidth = MIN(floor(theme.lineLength), _maxTextWidth); } } @@ -1376,57 +1416,53 @@ - (void)show { } //Break line if the text is too long, based on screen size. - NSTextContainer *textContainer; - if (@available(macOS 12.0, *)) { - textContainer = _view.layoutManager.textContainer; - } else { - textContainer = _view.textView.textContainer; - } - [textContainer setLineFragmentPadding:theme.hilitedCornerRadius]; + NSTextContainer *textContainer = _view.textView.textContainer; + CGFloat linePadding = textContainer.lineFragmentPadding; + NSEdgeInsets insets = _view.insets; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = [_screen visibleFrame]; - CGFloat maxTextHeight = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - theme.edgeInset.height * 2; - [textContainer setSize:NSMakeSize(_maxTextWidth + theme.hilitedCornerRadius * 2, maxTextHeight)]; + CGFloat maxTextHeight = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; + [textContainer setSize:NSMakeSize(_maxTextWidth + linePadding * 2, maxTextHeight)]; + // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; - NSRect maxContentRect = NSInsetRect(contentRect, theme.hilitedCornerRadius, 0); + NSRect maxContentRect = NSInsetRect(contentRect, linePadding, 0); if (theme.lineLength > 0) { // fixed line length / text width - if (_maxSize.width > 0) { // only applicable to non-status + if (_maxSize.width > 0) { // not applicable to status message where maxSize is set to 0 maxContentRect.size.width = _maxTextWidth; } } if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) : - (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : - (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + theme.hilitedCornerRadius + theme.edgeInset.width > NSMaxX(screenRect)))) && + if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) + : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) + : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + linePadding + insets.right > NSMaxX(screenRect)))) && theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - [textContainer setSize:NSMakeSize(_maxSize.width + theme.hilitedCornerRadius * 2, maxTextHeight)]; + [textContainer setSize:NSMakeSize(_maxSize.width + linePadding * 2, maxTextHeight)]; } } - if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? kOffsetHeight : 0)) : - (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + theme.edgeInset.height * 2 + (sweepVertical ? 0 : kOffsetHeight))) { + if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) + : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : kOffsetHeight))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { maxContentRect.size.height = _maxSize.height; - [textContainer setSize:NSMakeSize(_maxTextWidth + theme.hilitedCornerRadius * 2, _maxSize.height)]; + [textContainer setSize:NSMakeSize(_maxTextWidth + linePadding * 2, _maxSize.height)]; } } } - // the sweep direction of the client app changes the behavior of adjusting squirrel panel position NSRect windowRect; if (theme.vertical) { - windowRect.size = NSMakeSize(NSHeight(maxContentRect) + theme.edgeInset.height * 2, - NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2); + windowRect.size = NSMakeSize(NSHeight(maxContentRect) + insets.top + insets.bottom, + NSWidth(maxContentRect) + linePadding * 2 + insets.left + insets.right); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? theme.edgeInset.width+theme.hilitedCornerRadius : -kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left + linePadding : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } @@ -1434,11 +1470,11 @@ - (void)show { windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += NSHeight(preeditRect) + theme.edgeInset.height; + windowRect.origin.x += NSHeight(preeditRect) + insets.top; } } else { - windowRect.size = NSMakeSize(NSWidth(maxContentRect) + (theme.edgeInset.width + theme.hilitedCornerRadius) * 2, - NSHeight(maxContentRect) + theme.edgeInset.height * 2); + windowRect.size = NSMakeSize(NSWidth(maxContentRect) + linePadding * 2 + insets.left + insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { @@ -1448,22 +1484,22 @@ - (void)show { } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - theme.edgeInset.width - theme.hilitedCornerRadius, + windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left - linePadding, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (sweepVertical ? NSMinX(_position)-kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = sweepVertical ? NSMaxX(_position)+kOffsetHeight : NSMinX(screenRect); + windowRect.origin.x = sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = sweepVertical ? NSMinY(screenRect) : NSMaxY(_position)+kOffsetHeight; + windowRect.origin.y = sweepVertical ? NSMinY(screenRect) : NSMaxY(_position) + kOffsetHeight; } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMaxY(screenRect) : NSMinY(_position)-kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = (sweepVertical ? NSMaxY(screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); } if (theme.vertical) { @@ -1473,35 +1509,35 @@ - (void)show { windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); } - [self setFrame:[_screen backingAlignedRect:windowRect - options:NSAlignAllEdgesOutward] display:YES]; + // rotate the view, the core in vertical mode! if (theme.vertical) { + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward|NSAlignMaxYInward|NSAlignWidthNearest|NSAlignHeightNearest] display:YES]; [self.contentView setBoundsRotation:-90.0]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward|NSAlignMaxYInward|NSAlignWidthNearest|NSAlignHeightNearest] display:YES]; [self.contentView setBoundsRotation:0.0]; - [self.contentView setBoundsOrigin:NSMakePoint(0.0, 0.0)]; + [self.contentView setBoundsOrigin:NSZeroPoint]; } - [_view.textView setBoundsRotation:0.0]; + NSRect frameRect = [self.contentView backingAlignedRect:self.contentView.bounds + options:NSAlignMinXInward|NSAlignMaxYOutward|NSAlignWidthNearest|NSAlignHeightNearest]; + NSRect textFrameRect = NSMakeRect(NSMinX(frameRect) + insets.left, NSMinY(frameRect) + insets.bottom, + NSWidth(frameRect) - insets.left - insets.right, + NSHeight(frameRect) - insets.top - insets.bottom); if (@available(macOS 12.0, *)) { - NSPoint textViewOrigin = _view.layoutManager.usageBoundsForTextContainer.origin; - if (_view.candidateRanges.count > 0 && _view.preeditRange.length == 0) { - textViewOrigin.y += theme.linespace / 2; - } - [_view.textView setBoundsOrigin:textViewOrigin]; - } else { - [_view.textView setBoundsOrigin:_view.textView.textContainerOrigin]; + textFrameRect = NSInsetRect(textFrameRect, linePadding, 0); } - NSRect boundsRect = [self backingAlignedRect:self.contentView.bounds - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - [_view setFrame:boundsRect]; - [_view.textView setFrame:boundsRect]; + [_view.textView setBoundsRotation:0.0]; + [_view setBoundsOrigin:NSZeroPoint]; + [_view.textView setBoundsOrigin:NSZeroPoint]; + [_view setFrame:frameRect]; + [_view.textView setFrame:textFrameRect]; CGFloat translucency = theme.translucency; if (@available(macOS 10.14, *)) { if (translucency > 0) { - [_back setFrame:boundsRect]; + [_back setFrame:frameRect]; [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; } else { @@ -1509,7 +1545,6 @@ - (void)show { } } [self setAlphaValue:theme.alpha]; - [self invalidateShadow]; [self orderFront:nil]; // voila ! } @@ -1523,18 +1558,58 @@ - (void)hide { _maxSize = NSZeroSize; } -static CGFloat stringWidth(NSAttributedString *string, BOOL vertical){ - NSTextStorage *textStorage = [[NSTextStorage alloc] init]; - NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(FLT_MAX, FLT_MAX)]; - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; - [layoutManager addTextContainer:textContainer]; - [textStorage addLayoutManager:layoutManager]; - [textContainer setLineFragmentPadding:0.0]; - NSTextView *textView = textContainer.textView; - [textView setLayoutOrientation:vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; - [textStorage setAttributedString:string]; - [layoutManager ensureLayoutForTextContainer:textContainer]; - return NSWidth([layoutManager usedRectForTextContainer:textContainer]); +- (BOOL)shouldBreakLineWithRange:(NSRange)range { + if (@available(macOS 12.0, *)) { + NSTextRange *textRange = [_view getTextRangeFromRange:range]; + NSUInteger __block lineCount = 0; + [_view.layoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: + ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { + ++lineCount; + return YES; + }]; + return lineCount > 1; + } else { + NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range + actualCharacterRange:NULL]; + NSUInteger loc = glyphRange.location; + NSRange lineRange = NSMakeRange(loc, 0); + NSUInteger lineCount = 0; + while (loc < NSMaxRange(glyphRange)) { + [_view.textView.layoutManager lineFragmentRectForGlyphAtIndex:loc + effectiveRange:&lineRange]; + ++lineCount; + loc = NSMaxRange(lineRange); + } + return lineCount > 1; + } +} + +- (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLength{ + if (@available(macOS 12.0, *)) { + NSTextRange *textRange = [_view getTextRangeFromRange:range]; + CGFloat __block rangeEdge; + [_view.layoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: + ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { + rangeEdge = NSMaxX(segmentFrame); + return YES; + }]; + NSRect container = [_view.layoutManager usageBoundsForTextContainer]; + *maxLineLength = MAX(MIN(_maxTextWidth, ceil(NSWidth(container))), _maxSize.width); + return NSMinX(container) + *maxLineLength > rangeEdge; + } else { + NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; + CGFloat rangeEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); + NSRect container = NSInsetRect([_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer], + _view.textView.textContainer.lineFragmentPadding, 0); + *maxLineLength = MAX(MIN(_maxTextWidth, ceil(NSWidth(container))), _maxSize.width); + return NSMinX(container) + *maxLineLength > rangeEdge; + } } // Main function to add attributes to text output from librime @@ -1591,101 +1666,74 @@ - (void)showPreedit:(NSString *)preedit SquirrelTheme *theme = _view.currentTheme; [self getMaxTextWidth]; if (theme.lineLength > 0) { - _maxSize.width = MIN(theme.lineLength, _maxTextWidth); + _maxSize.width = MIN(floor(theme.lineLength), _maxTextWidth); } + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, + theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width); - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] init]; + [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; + NSTextStorage *text = _view.textView.textStorage; + [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); - CGFloat preeditWidth = 0.0; // preedit if (preedit) { NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringToIndex:selRange.location].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + initWithString:[preedit substringToIndex:selRange.location] + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { NSUInteger highlightedPreeditStart = preeditLine.length; [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringWithRange:selRange].precomposedStringWithCanonicalMapping - attributes:theme.preeditHighlightedAttrs]]; - highlightedPreeditRange = NSMakeRange(text.length + highlightedPreeditStart, + initWithString:[preedit substringWithRange:selRange] + attributes:theme.preeditHighlightedAttrs]]; + highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); } if (NSMaxRange(selRange) < preedit.length) { [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringFromIndex:NSMaxRange(selRange)].precomposedStringWithCanonicalMapping - attributes:theme.preeditAttrs]]; + initWithString:[preedit substringFromIndex:NSMaxRange(selRange)] + attributes:theme.preeditAttrs]]; } // force caret to be rendered horizontally in vertical layout if (caretPos != NSNotFound) { [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@NO - range:NSMakeRange(caretPos, 1)]; + range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } [preeditLine addAttribute:NSParagraphStyleAttributeName value:theme.preeditParagraphStyle range:NSMakeRange(0, preeditLine.length)]; - preeditRange = NSMakeRange(text.length, preeditLine.length); + preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; - preeditWidth = stringWidth(preeditLine, theme.vertical); if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; + } else { + insets.bottom = theme.edgeInset.height; } + insets.top = theme.edgeInset.height; } - // prepare paging and separator for width calculation but no insertion yet - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; - NSGlyphInfo *backFilled = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"◀"]; - NSGlyphInfo *backOutline = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"◁"]; - NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] - initWithString:(pageNum ? @"◀" : @"◁") - attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; - [pageUpString addAttribute:NSGlyphInfoAttributeName - value:(pageNum ? backFilled : backOutline) - range:NSMakeRange(0, pageUpString.length)]; - [paging appendAttributedString:pageUpString]; - - [paging appendAttributedString:[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@" %lu ", pageNum+1] - attributes:theme.pagingAttrs]]; - - NSGlyphInfo *forwardOutline = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" - forFont:theme.pagingAttrs[NSFontAttributeName] baseString:@"▷"]; - NSGlyphInfo *forwardFilled = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" - forFont:theme.pagingAttrs[NSFontAttributeName] baseString:@"▶"]; - NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] - initWithString:(lastPage ? @"▷" : @"▶") - attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; - [pageDownString addAttribute:NSGlyphInfoAttributeName - value:(lastPage ? forwardOutline : forwardFilled) - range:NSMakeRange(0, pageDownString.length)]; - [paging appendAttributedString:pageDownString]; - CGFloat pagingWidth = theme.showPaging ? stringWidth(paging, theme.vertical) : 0.0; - - NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; + // separator + NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; [sep addAttribute:NSVerticalGlyphFormAttributeName value:@NO range:NSMakeRange(0, sep.length)]; - CGFloat separatorWidth = theme.linear ? stringWidth(sep, theme.vertical) : 0.0; - _view.seperatorWidth = separatorWidth; - - CGFloat lineWidth = 0.0 - separatorWidth; - CGFloat maxLineWidth = MIN(preeditWidth, _maxTextWidth); - BOOL useTab = NO; + [sep fixAttributesInRange:NSMakeRange(0, sep.length)]; + CGFloat separatorWidth = theme.linear ? NSWidth([sep boundingRectWithSize:NSZeroSize options:0]) : 0.0; + _view.separatorWidth = separatorWidth; - // candidates + // candidate items NSUInteger candidateBlockStart = text.length; NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; + NSUInteger lineStart = text.length; + NSRange separatorRange = NSMakeRange(NSNotFound, 0); for (NSUInteger i = 0; i < candidates.count; ++i) { - NSMutableAttributedString *line = [[NSMutableAttributedString alloc] init]; + NSMutableAttributedString *item = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; NSDictionary *labelAttrs = (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; @@ -1696,150 +1744,188 @@ - (void)showPreedit:(NSString *)preedit NSString *prefixLabelString; NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; if (labels.count > 1 && i < labels.count) { - prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } - [line appendAttributedString:[[NSAttributedString alloc] + [item appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString - attributes:labelAttrs]]; + attributes:labelAttrs]]; if (!theme.linear) { // get the label size for indent - labelWidth = stringWidth(line, theme.vertical); + labelWidth = NSWidth([item boundingRectWithSize:NSZeroSize options:0]); } } - NSUInteger candidateStart = line.length; + NSUInteger candidateStart = item.length; NSString *candidate = candidates[i]; - [line appendAttributedString:[[NSAttributedString alloc] initWithString:candidate.precomposedStringWithCanonicalMapping - attributes:attrs]]; + [item appendAttributedString:[[NSAttributedString alloc] + initWithString:candidate + attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - [line addAttribute:NSWritingDirectionAttributeName + [item addAttribute:NSWritingDirectionAttributeName value:@[@0] - range:NSMakeRange(candidateStart, line.length-candidateStart)]; + range:NSMakeRange(candidateStart, item.length - candidateStart)]; if (i < comments.count && [comments[i] length] != 0) { - [line appendAttributedString:[[NSAttributedString alloc] initWithString:@" " - attributes:commentAttrs]]; + [item appendAttributedString:[[NSAttributedString alloc] + initWithString:@" " + attributes:commentAttrs]]; NSString *comment = comments[i]; - [line appendAttributedString:[[NSAttributedString alloc] initWithString:comment.precomposedStringWithCanonicalMapping - attributes:commentAttrs]]; + [item appendAttributedString:[[NSAttributedString alloc] + initWithString:comment + attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { NSString *suffixLabelString; NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; if (labels.count > 1 && i < labels.count) { - suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]].precomposedStringWithCanonicalMapping; + suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; } else if (labels.count == 1 && i < [labels[0] length]) { // custom: A. B. C... NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } else { // default: 1. 2. 3... NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; } - [line appendAttributedString:[[NSAttributedString alloc] + [item appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString - attributes:labelAttrs]]; + attributes:labelAttrs]]; } NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - // determine if the line is too wide and line break is needed, based on screen size. - NSString *separtatorString = @"\n"; - CGFloat candidateWidth = stringWidth(line, theme.vertical); - - if (theme.linear) { - if (i == numCandidates-1) { - candidateWidth += separatorWidth + pagingWidth; - } - if (lineWidth + separatorWidth + candidateWidth > _maxTextWidth) { - separtatorString = @"\n"; - maxLineWidth = MAX(maxLineWidth, MIN(lineWidth, _maxTextWidth)); - lineWidth = candidateWidth; - } else { - separtatorString = @" "; - lineWidth += separatorWidth + candidateWidth; - } - } else { // stacked candidates - maxLineWidth = MAX(maxLineWidth, MIN(candidateWidth, _maxTextWidth)); + if (!theme.linear) { paragraphStyleCandidate.headIndent = labelWidth; } - if (i == numCandidates-1 && theme.showPaging) { - maxLineWidth = MAX(maxLineWidth, _maxSize.width); - useTab = !((theme.linear ? lineWidth : pagingWidth) >= maxLineWidth); - } - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] - initWithString:separtatorString - attributes:theme.attrs]; - [separator addAttribute:NSVerticalGlyphFormAttributeName - value:@NO - range:NSMakeRange(0, separator.length)]; - + [item addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleCandidate + range:NSMakeRange(0, item.length)]; + // determine if the line is too wide and line break is needed, based on screen size. + NSString *separatorString = theme.linear ? @" " : @"\n"; if (i > 0) { + NSUInteger separatorStart = text.length; + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] + initWithString:separatorString + attributes:theme.attrs]; + [separator addAttribute:NSVerticalGlyphFormAttributeName + value:@NO + range:NSMakeRange(0, separator.length)]; + separatorRange = NSMakeRange(separatorStart, separator.length); [text appendAttributedString:separator]; + [text appendAttributedString:item]; + if (theme.linear && [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { + [text replaceCharactersInRange:separatorRange withString:@"\n"]; + lineStart = separatorStart + 1; + separatorRange = NSMakeRange(NSNotFound, 0); + } + } else { + [text appendAttributedString:item]; } - [line addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, line.length)]; - [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length, line.length)]]; - [text appendAttributedString:line]; + [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length - item.length, item.length)]]; } // paging indication NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates > 0 && theme.showPaging) { + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; + NSGlyphInfo *backFill = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"◀"]; + NSGlyphInfo *backStroke = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"◁"]; + NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] + initWithString:(pageNum ? @"◀" : @"◁") + attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; + [pageUpString addAttribute:NSGlyphInfoAttributeName + value:(pageNum ? backFill : backStroke) + range:NSMakeRange(0, pageUpString.length)]; + [paging appendAttributedString:pageUpString]; + + [paging appendAttributedString:[[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] + attributes:theme.pagingAttrs]]; + + NSGlyphInfo *forwardStroke = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"▷"]; + NSGlyphInfo *forwardFill = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" + forFont:theme.pagingAttrs[NSFontAttributeName] + baseString:@"▶"]; + NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] + initWithString:(lastPage ? @"▷" : @"▶") + attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; + [pageDownString addAttribute:NSGlyphInfoAttributeName + value:(lastPage ? forwardStroke : forwardFill) + range:NSMakeRange(0, pageDownString.length)]; + [paging appendAttributedString:pageDownString]; + [text appendAttributedString:[[NSAttributedString alloc] - initWithString:theme.linear ? (useTab ? @"\t" : @" ") : @"\n" - attributes:theme.attrs]]; + initWithString:theme.linear ? @" " : @"\n" + attributes:theme.attrs]]; NSUInteger pagingStart = text.length; + CGFloat maxLineLength; if (theme.linear) { [text appendAttributedString:paging]; + if ([self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { + if (separatorRange.length > 0) { + [text replaceCharactersInRange:separatorRange withString:@"\n"]; + } else { + [text insertAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs] atIndex:pagingStart-1]; + pagingStart += 1; + } + } NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType - location:maxLineWidth]]; + if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, text.length - pagingStart) maxLineLength:&maxLineLength]) { + [text replaceCharactersInRange:NSMakeRange(pagingStart-1, 1) withString:@"\t"]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; + } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging - range:NSMakeRange(candidateBlockStart, text.length-candidateBlockStart)]; + range:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; - if (useTab) { + if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { [paging replaceCharactersInRange:NSMakeRange(1, 1) withString:@"\t"]; - [paging replaceCharactersInRange:NSMakeRange(paging.length-2, 1) withString:@"\t"]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType - location:maxLineWidth/2], - [[NSTextTab alloc] initWithType:NSRightTabStopType - location:maxLineWidth]]; + [paging replaceCharactersInRange:NSMakeRange(paging.length - 2, 1) withString:@"\t"]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength/2], + [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; } + NSFont *pagingFont = theme.pagingAttrs[NSFontAttributeName]; [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; [text appendAttributedString:paging]; + insets.bottom = theme.edgeInset.height; } - pagingRange = NSMakeRange(pagingStart, paging.length); + pagingRange = NSMakeRange(text.length - paging.length, paging.length); } - [_view.textView.textStorage setAttributedString:text]; - [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; - // text done! - [_view drawViewWith:candidateRanges - highlightedIndex:index - preeditRange:preeditRange - highlightedPreeditRange:highlightedPreeditRange - pagingRange:pagingRange - pagingButton:turnPage]; + [_view drawViewWithInsets:insets + candidateRanges:candidateRanges + highlightedIndex:index + preeditRange:preeditRange + highlightedPreeditRange:highlightedPreeditRange + pagingRange:pagingRange + pagingButton:turnPage]; [self show]; } @@ -1866,29 +1952,30 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { [self getMaxTextWidth]; SquirrelTheme *theme = _view.currentTheme; - NSMutableAttributedString *text = [[NSMutableAttributedString alloc] - initWithString:message.precomposedStringWithCanonicalMapping - attributes:theme.commentAttrs]; + _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width, + theme.edgeInset.height, theme.edgeInset.width); + [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; + + NSTextStorage *text = _view.textView.textStorage; + [text setAttributedString:[[NSMutableAttributedString alloc] init]]; + [text appendAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]]; [text addAttribute:NSParagraphStyleAttributeName value:theme.statusParagraphStyle range:NSMakeRange(0, text.length)]; - [_view.textView.textStorage setAttributedString:text]; - [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; - - _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages - NSRange emptyRange = NSMakeRange(NSNotFound, 0); - [_view drawViewWith:@[] - highlightedIndex:NSNotFound - preeditRange:emptyRange - highlightedPreeditRange:emptyRange - pagingRange:emptyRange - pagingButton:NSNotFound]; - [self show]; - if (_statusTimer) { [_statusTimer invalidate]; } + [_view drawViewWithInsets:insets + candidateRanges:@[] + highlightedIndex:NSNotFound + preeditRange:NSMakeRange(NSNotFound, 0) + highlightedPreeditRange:NSMakeRange(NSNotFound, 0) + pagingRange:NSMakeRange(NSNotFound, 0) + pagingButton:NSNotFound]; + [self show]; _statusTimer = [NSTimer scheduledTimerWithTimeInterval:kShowStatusDuration target:self selector:@selector(hideStatus:) @@ -1900,8 +1987,8 @@ - (void)hideStatus:(NSTimer *)timer { [self hide]; } -static inline NSColor *blendColors(NSColor *foregroundColor, - NSColor *backgroundColor) { +static inline NSColor * blendColors(NSColor *foregroundColor, + NSColor *backgroundColor) { if (!backgroundColor) { // return foregroundColor; backgroundColor = [NSColor lightGrayColor]; } @@ -1910,19 +1997,19 @@ - (void)hideStatus:(NSTimer *)timer { colorWithAlphaComponent:foregroundColor.alphaComponent]; } -static inline NSColor *inverseColor(NSColor *color) { +static inline NSColor * inverseColor(NSColor *color) { if (color == nil) { return nil; } else { return [NSColor colorWithColorSpace:color.colorSpace hue:color.hueComponent saturation:color.saturationComponent - brightness:1-color.brightnessComponent + brightness:1 - color.brightnessComponent alpha:color.alphaComponent]; } } -static NSFontDescriptor *getFontDescriptor(NSString *fullname) { +static NSFontDescriptor * getFontDescriptor(NSString *fullname) { if (fullname == nil) { return nil; } @@ -1944,12 +2031,13 @@ - (void)hideStatus:(NSTimer *)timer { return nil; } NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef) @"zh"); + CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef)@"zh"); NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); CFRelease(systemFontRef); NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count-1)] arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; - NSDictionary *attributes = @{NSFontCascadeListAttribute:fallbackDescriptors}; + NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] + arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + NSDictionary *attributes = @{NSFontCascadeListAttribute: fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } @@ -1957,24 +2045,20 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { if (vertical) { font = font.verticalFont; } - CGFloat lineHeight = font.ascender - font.descender; - if (@available(macOS 12.0, *)) { - return lineHeight; - } else { - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; - for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; - if (vertical) { - fallbackFont = fallbackFont.verticalFont; - } - lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); + CGFloat lineHeight = ceil(font.ascender - font.descender); + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; } - return lineHeight; + lineHeight = MAX(lineHeight, ceil(MIN(fallbackFont.ascender - fallbackFont.descender, NSHeight([fallbackFont boundingRectForFont])))); } + return lineHeight; } static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfig *config, NSString *prefix) { - NSString* candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; + NSString *candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { *isLinearCandidateList = false; } else if ([candidateListLayout isEqualToString:@"linear"]) { @@ -1989,7 +2073,7 @@ static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfi } static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, NSString *prefix) { - NSString* textOrientation = [config getString:[prefix stringByAppendingString:@"/text_orientation"]]; + NSString *textOrientation = [config getString:[prefix stringByAppendingString:@"/text_orientation"]]; if ([textOrientation isEqualToString:@"horizontal"]) { *isVerticalText = false; } else if ([textOrientation isEqualToString:@"vertical"]) { @@ -2020,19 +2104,19 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSString *candidateFormat = [config getString:@"style/candidate_format"]; NSString *fontName = [config getString:@"style/font_face"]; - CGFloat fontSize = [config getDouble:@"style/font_point"]; + CGFloat fontSize = MAX([config getDouble:@"style/font_point"], 0.0); NSString *labelFontName = [config getString:@"style/label_font_face"]; - CGFloat labelFontSize = [config getDouble:@"style/label_font_point"]; + CGFloat labelFontSize = MAX([config getDouble:@"style/label_font_point"], 0.0); NSString *commentFontName = [config getString:@"style/comment_font_face"]; - CGFloat commentFontSize = [config getDouble:@"style/comment_font_point"]; + CGFloat commentFontSize = MAX([config getDouble:@"style/comment_font_point"], 0.0); CGFloat alpha = MIN(MAX([config getDouble:@"style/alpha"], 0.0), 1.0); CGFloat translucency = MIN(MAX([config getDouble:@"style/translucency"], 0.0), 1.0); - CGFloat cornerRadius = [config getDouble:@"style/corner_radius"]; - CGFloat hilitedCornerRadius = [config getDouble:@"style/hilited_corner_radius"]; - CGFloat borderHeight = [config getDouble:@"style/border_height"]; - CGFloat borderWidth = [config getDouble:@"style/border_width"]; - CGFloat lineSpacing = [config getDouble:@"style/line_spacing"]; - CGFloat spacing = [config getDouble:@"style/spacing"]; + CGFloat cornerRadius = MAX([config getDouble:@"style/corner_radius"], 0.0); + CGFloat hilitedCornerRadius = MAX([config getDouble:@"style/hilited_corner_radius"], 0.0); + CGFloat borderHeight = MAX([config getDouble:@"style/border_height"], 0.0); + CGFloat borderWidth = MAX([config getDouble:@"style/border_width"], 0.0); + CGFloat lineSpacing = MAX([config getDouble:@"style/line_spacing"], 0.0); + CGFloat spacing = MAX([config getDouble:@"style/spacing"], 0.0); CGFloat baseOffset = [config getDouble:@"style/base_offset"]; CGFloat lineLength = MAX([config getDouble:@"style/line_length"], 0.0); @@ -2119,7 +2203,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } NSNumber *fontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; if (fontSizeOverridden) { - fontSize = fontSizeOverridden.doubleValue; + fontSize = MAX(fontSizeOverridden.doubleValue, 0.0); } NSString *labelFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; if (labelFontNameOverridden) { @@ -2127,7 +2211,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } NSNumber *labelFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; if (labelFontSizeOverridden) { - labelFontSize = labelFontSizeOverridden.doubleValue; + labelFontSize = MAX(labelFontSizeOverridden.doubleValue, 0.0); } NSString *commentFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; if (commentFontNameOverridden) { @@ -2135,7 +2219,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } NSNumber *commentFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; if (commentFontSizeOverridden) { - commentFontSize = commentFontSizeOverridden.doubleValue; + commentFontSize = MAX(commentFontSizeOverridden.doubleValue, 0.0); } NSColor *candidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_color"]]; if (candidateLabelColorOverridden) { @@ -2159,27 +2243,27 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } NSNumber *cornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]]; if (cornerRadiusOverridden) { - cornerRadius = cornerRadiusOverridden.doubleValue; + cornerRadius = MAX(cornerRadiusOverridden.doubleValue, 0.0); } NSNumber *hilitedCornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]]; if (hilitedCornerRadiusOverridden) { - hilitedCornerRadius = hilitedCornerRadiusOverridden.doubleValue; + hilitedCornerRadius = MAX(hilitedCornerRadiusOverridden.doubleValue, 0.0); } NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; if (borderHeightOverridden) { - borderHeight = borderHeightOverridden.doubleValue; + borderHeight = MAX(borderHeightOverridden.doubleValue, 0.0); } NSNumber *borderWidthOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]]; if (borderWidthOverridden) { - borderWidth = borderWidthOverridden.doubleValue; + borderWidth = MAX(borderWidthOverridden.doubleValue, 0.0); } NSNumber *lineSpacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]]; if (lineSpacingOverridden) { - lineSpacing = lineSpacingOverridden.doubleValue; + lineSpacing = MAX(lineSpacingOverridden.doubleValue, 0.0); } NSNumber *spacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]]; if (spacingOverridden) { - spacing = spacingOverridden.doubleValue; + spacing = MAX(spacingOverridden.doubleValue, 0.0); } NSNumber *baseOffsetOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]]; if (baseOffsetOverridden) { @@ -2196,15 +2280,21 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f commentFontSize = commentFontSize ? commentFontSize : fontSize; NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); - NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? fontDescriptor : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) size:fontSize]; + NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? fontDescriptor : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) + size:fontSize]; NSFontDescriptor *labelFontDescriptor = getFontDescriptor(labelFontName); - NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] : (fontDescriptor ? [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize] : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]); + NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] + : (fontDescriptor ? [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize] + : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); - NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? commentFontDescriptor : fontDescriptor) size:commentFontSize]; + NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? commentFontDescriptor : fontDescriptor) + size:commentFontSize]; - NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:labelFontSize]; + NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] + fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] + size:labelFontSize]; CGFloat fontHeight = getLineHeight(font, vertical); CGFloat labelFontHeight = getLineHeight(labelFont, vertical); @@ -2223,8 +2313,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; - pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; - pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; + pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); + pagingParagraphStyle.maximumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); pagingParagraphStyle.paragraphSpacingBefore = pagingFont.leading; NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; @@ -2272,18 +2362,18 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f ((backgroundColor.brightnessComponent >= 0.5 && isDark) || (backgroundColor.brightnessComponent < 0.5 && !isDark))) { backgroundColor = inverseColor(backgroundColor); - borderColor = inverseColor(borderColor) ; + borderColor = inverseColor(borderColor); preeditBackgroundColor = inverseColor(preeditBackgroundColor); candidateTextColor = inverseColor(candidateTextColor); highlightedCandidateTextColor = [inverseColor(highlightedCandidateTextColor) highlightWithLevel:highlightedCandidateTextColor.brightnessComponent]; - highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1-highlightedCandidateBackColor.brightnessComponent]; + highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1 - highlightedCandidateBackColor.brightnessComponent]; candidateLabelColor = inverseColor(candidateLabelColor); highlightedCandidateLabelColor = [inverseColor(highlightedCandidateLabelColor) highlightWithLevel:highlightedCandidateLabelColor.brightnessComponent]; commentTextColor = inverseColor(commentTextColor); highlightedCommentTextColor = [inverseColor(highlightedCommentTextColor) highlightWithLevel:highlightedCommentTextColor.brightnessComponent]; textColor = inverseColor(textColor); highlightedTextColor = [inverseColor(highlightedTextColor) highlightWithLevel:highlightedTextColor.brightnessComponent]; - highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1-highlightedBackColor.brightnessComponent]; + highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1 - highlightedBackColor.brightnessComponent]; } } @@ -2319,16 +2409,16 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f [theme setStatusMessageType:statusMessageType]; - [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs - labelAttrs:labelAttrs - labelHighlightedAttrs:labelHighlightedAttrs - commentAttrs:commentAttrs - commentHighlightedAttrs:commentHighlightedAttrs - preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs]; + [theme setAttrs:attrs + highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs + labelHighlightedAttrs:labelHighlightedAttrs + commentAttrs:commentAttrs + commentHighlightedAttrs:commentHighlightedAttrs + preeditAttrs:preeditAttrs + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle @@ -2344,8 +2434,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:cornerRadius - hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/2) + [theme setCornerRadius:MIN(cornerRadius, lineHeight/2) + hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/3) edgeInset:edgeInset linespace:lineSpacing preeditLinespace:spacing From d076609494242151c6b6284834c73d2f77620494 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 29 Jul 2023 12:06:22 +0200 Subject: [PATCH 120/164] fix inline modes bugs apply proper style for inline preedit and inline candidate fix wrong selection ranges and cursor position under inline mode --- SquirrelInputController.m | 213 +++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 107 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 08c79057f..f220fa938 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -7,12 +7,12 @@ #import #import -@interface SquirrelInputController(Private) --(void)createSession; --(void)destroySession; --(void)rimeConsumeCommittedText; --(void)rimeUpdate; --(void)updateAppOptions; +@interface SquirrelInputController (Private) +- (void)createSession; +- (void)destroySession; +- (void)rimeConsumeCommittedText; +- (void)rimeUpdate; +- (void)updateAppOptions; @end const int N_KEY_ROLL_OVER = 50; @@ -45,7 +45,7 @@ @implementation SquirrelInputController { @abstract Receive incoming event @discussion This method receives key events from the client application. */ -- (BOOL)handleEvent:(NSEvent*)event client:(id)sender +- (BOOL)handleEvent:(NSEvent *)event client:(id)sender { // Return YES to indicate the the key input was received and dealt with. // Key processing will not continue in that case. In other words the @@ -64,7 +64,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender } } - NSString* app = [_currentClient bundleIdentifier]; + NSString *app = [_currentClient bundleIdentifier]; if (![_currentApp isEqualToString:app]) { _currentApp = [app copy]; @@ -138,7 +138,8 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender // translate osx keyevents to rime keyevents int rime_keycode = osx_keycode_to_rime_keycode(keyCode, keyChars.UTF8String[0], - modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); + modifiers & OSX_SHIFT_MASK, + modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) @@ -157,7 +158,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender return handled; } --(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers +- (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers { // TODO add special key event preprocessing here @@ -179,9 +180,9 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (!handled) { BOOL isVimBackInCommandMode = rime_keycode == XK_Escape || - ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || - rime_keycode == XK_C || - rime_keycode == XK_bracketleft)); + ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || + rime_keycode == XK_C || + rime_keycode == XK_bracketleft)); if (isVimBackInCommandMode && rime_get_api()->get_option(_session, "vim_mode") && !rime_get_api()->get_option(_session, "ascii_mode")) { @@ -192,16 +193,15 @@ -(BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers // Simulate key-ups for every interesting key-down for chord-typing. if (handled) { - bool is_chording_key = - (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || - rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + BOOL is_chording_key = + (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || + rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || + rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; - } - else if ((rime_modifiers & kReleaseMask) == 0) { + } else if ((rime_modifiers & kReleaseMask) == 0) { // non-chording key pressed [self clearChord]; } @@ -231,17 +231,17 @@ - (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index { return handled; } --(void)onChordTimer:(NSTimer *)timer +- (void)onChordTimer:(NSTimer *)timer { // chord release triggered by timer int processed_keys = 0; if (_chordKeyCount && _session) { // simulate key-ups for (int i = 0; i < _chordKeyCount; ++i) { - if (rime_get_api()->process_key(_session, - _chordKeyCodes[i], - (_chordModifiers[i] | kReleaseMask))) + if (rime_get_api()->process_key(_session, _chordKeyCodes[i], + (_chordModifiers[i] | kReleaseMask))) { ++processed_keys; + } } } [self clearChord]; @@ -250,7 +250,7 @@ -(void)onChordTimer:(NSTimer *)timer } } --(void)updateChord:(int)keycode modifiers:(int)modifiers +- (void)updateChord:(int)keycode modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); for (int i = 0; i < _chordKeyCount; ++i) { @@ -280,7 +280,7 @@ -(void)updateChord:(int)keycode modifiers:(int)modifiers repeats:NO]; } --(void)clearChord +- (void)clearChord { _chordKeyCount = 0; if (_chordTimer) { @@ -291,13 +291,13 @@ -(void)clearChord } } --(NSUInteger)recognizedEvents:(id)sender +- (NSUInteger)recognizedEvents:(id)sender { //NSLog(@"recognizedEvents:"); return NSEventMaskKeyDown | NSEventMaskFlagsChanged; } --(void)activateServer:(id)sender +- (void)activateServer:(id)sender { //NSLog(@"activateServer:"); NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; @@ -314,7 +314,7 @@ -(void)activateServer:(id)sender _preeditString = @""; } --(instancetype)initWithServer:(IMKServer*)server delegate:(id)delegate client:(id)inputClient +- (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient { //NSLog(@"initWithServer:delegate:client:"); if (self = [super initWithServer:server delegate:delegate client:inputClient]) { @@ -324,7 +324,7 @@ -(instancetype)initWithServer:(IMKServer*)server delegate:(id)delegate client:(i return self; } --(void)deactivateServer:(id)sender +- (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); [NSApp.squirrelAppDelegate.panel hide]; @@ -342,7 +342,7 @@ -(void)deactivateServer:(id)sender to clean up if that is necessary. */ --(void)commitComposition:(id)sender +- (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session && _preeditString.length > 0) { @@ -356,57 +356,57 @@ -(void)commitComposition:(id)sender // > though we specified the showPrefPanel: in SunPinyinApplicationDelegate as the // > action receiver, the IMKInputController will actually receive the event. // so here we deliver messages to our responsible SquirrelApplicationDelegate --(void)deploy:(id)sender +- (void)deploy:(id)sender { [NSApp.squirrelAppDelegate deploy:sender]; } --(void)syncUserData:(id)sender +- (void)syncUserData:(id)sender { [NSApp.squirrelAppDelegate syncUserData:sender]; } --(void)configure:(id)sender +- (void)configure:(id)sender { [NSApp.squirrelAppDelegate configure:sender]; } --(void)checkForUpdates:(id)sender +- (void)checkForUpdates:(id)sender { [NSApp.squirrelAppDelegate.updater performSelector:@selector(checkForUpdates:) withObject:sender]; } --(void)openWiki:(id)sender +- (void)openWiki:(id)sender { [NSApp.squirrelAppDelegate openWiki:sender]; } --(NSMenu*)menu +- (NSMenu *)menu { return NSApp.squirrelAppDelegate.menu; } --(NSAttributedString *)originalString:(id)sender +- (NSAttributedString *)originalString:(id)sender { return _originalString; } --(id)composedString:(id)sender +- (id)composedString:(id)sender { return _composedString; } --(NSArray*)candidates:(id)sender +- (NSArray *)candidates:(id)sender { return _candidates; } --(void)dealloc +- (void)dealloc { [self destroySession]; } --(void)commitString:(NSString*)string +- (void)commitString:(NSString *)string { //NSLog(@"commitString:"); [_currentClient insertText:string @@ -419,15 +419,16 @@ -(void)commitString:(NSString*)string [NSApp.squirrelAppDelegate.panel hide]; } --(void)showPreeditString:(NSString*)preedit - selRange:(NSRange)range - caretPos:(NSUInteger)pos +- (void)showPreeditString:(NSString *)preedit + selRange:(NSRange)range + caretPos:(NSUInteger)pos { //NSLog(@"showPreeditString: '%@'", preedit); if ([_preeditString isEqualToString:preedit] && - _caretPos == pos && _selRange.location == range.location && _selRange.length == range.length) + NSEqualRanges(_selRange, range) && _caretPos == pos) { return; + } _preeditString = preedit; _selRange = range; @@ -435,39 +436,42 @@ -(void)showPreeditString:(NSString*)preedit //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", // range.location, range.length, pos); - NSDictionary* attrs; - NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:preedit]; + NSDictionary *attrs; + NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:preedit]; if (range.location > 0) { NSRange convertedRange = NSMakeRange(0, range.location); attrs = [self markForStyle:kTSMHiliteConvertedText atRange:convertedRange]; [attrString setAttributes:attrs range:convertedRange]; } - { - NSRange remainingRange = NSMakeRange(range.location, preedit.length - range.location); - attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:remainingRange]; - [attrString setAttributes:attrs range:remainingRange]; + if (range.location < pos) { + attrs = [self markForStyle:kTSMHiliteSelectedConvertedText atRange:range]; + [attrString setAttributes:attrs range:range]; + } + if (MIN(NSMaxRange(range), pos) < preedit.length) { + NSRange rawRange = NSMakeRange(MIN(NSMaxRange(range), pos), preedit.length - MIN(NSMaxRange(range), pos)); + attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; + [attrString setAttributes:attrs range:rawRange]; } [_currentClient setMarkedText:attrString selectionRange:NSMakeRange(pos, 0) replacementRange:NSMakeRange(NSNotFound, 0)]; - } --(void)showPanelWithPreedit:(NSString*)preedit - selRange:(NSRange)selRange - caretPos:(NSUInteger)caretPos - candidates:(NSArray*)candidates - comments:(NSArray*)comments - labels:(NSArray*)labels - highlighted:(NSUInteger)index - pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage +- (void)showPanelWithPreedit:(NSString *)preedit + selRange:(NSRange)selRange + caretPos:(NSUInteger)caretPos + candidates:(NSArray *)candidates + comments:(NSArray *)comments + labels:(NSArray *)labels + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; NSRect inputPos; [_currentClient attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; - SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; panel.position = inputPos; panel.inputController = self; [panel showPreedit:preedit @@ -487,11 +491,11 @@ -(void)showPanelWithPreedit:(NSString*)preedit // implementation of private interface -@implementation SquirrelInputController(Private) +@implementation SquirrelInputController (Private) --(void)createSession +- (void)createSession { - NSString* app = [_currentClient bundleIdentifier]; + NSString *app = [_currentClient bundleIdentifier]; NSLog(@"createSession: %@", app); _currentApp = [app copy]; _session = rime_get_api()->create_session(); @@ -503,13 +507,14 @@ -(void)createSession } } --(void)updateAppOptions +- (void)updateAppOptions { - if (!_currentApp) + if (!_currentApp) { return; - SquirrelAppOptions* appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:_currentApp]; + } + SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:_currentApp]; if (appOptions) { - for (NSString* key in appOptions) { + for (NSString *key in appOptions) { BOOL value = appOptions[key].boolValue; NSLog(@"set app option: %@ = %d", key, value); rime_get_api()->set_option(_session, key.UTF8String, value); @@ -517,7 +522,7 @@ -(void)updateAppOptions } } --(void)destroySession +- (void)destroySession { //NSLog(@"destroySession:"); if (_session) { @@ -527,24 +532,17 @@ -(void)destroySession [self clearChord]; } --(void)rimeConsumeCommittedText +- (void)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - [self commitString: commitText]; + [self commitString:commitText]; rime_get_api()->free_commit(&commit); } } -NSString *substr(const char *str, int length) { - char substring[length+1]; - strncpy(substring, str, length); - substring[length] = '\0'; - return @(substring); -} - --(void)rimeUpdate +- (void)rimeUpdate { //NSLog(@"rimeUpdate"); [self rimeConsumeCommittedText]; @@ -559,7 +557,7 @@ -(void)rimeUpdate // inline preedit _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || - rime_get_api()->get_option(_session, "inline"); + rime_get_api()->get_option(_session, "inline"); _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string @@ -578,10 +576,10 @@ -(void)rimeUpdate if (!preedit || switcher) { _composedString = @""; } else if (rime_get_api()->get_option(_session, "soft_cursor")) { - int caretPos = ctx.composition.cursor_pos; - char composed[strlen(preedit)-3]; - for (int i = 0; i < strlen(preedit)-3; ++i) { - composed[i] = preedit[i < caretPos ? i : i+3]; + int cursorPos = ctx.composition.cursor_pos; + char composed[strlen(preedit) - 3]; + for (int i = 0; i < strlen(preedit) - 3; ++i) { + composed[i] = preedit[i < cursorPos ? i : i + 3]; } _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; } else { @@ -592,35 +590,37 @@ -(void)rimeUpdate const char *raw_input = rime_get_api()->get_input(_session); _originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; - NSUInteger start = substr(preedit, ctx.composition.sel_start).length; - NSUInteger end = substr(preedit, ctx.composition.sel_end).length; - NSUInteger caretPos = substr(preedit, ctx.composition.cursor_pos).length; - NSRange selRange = NSMakeRange(start, end - start); - if (_inlineCandidate) { + NSUInteger start = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; + NSUInteger end = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; + NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; + if (_inlineCandidate && !switcher) { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; - if (_inlinePreedit && !switcher) { - if ((caretPos >= NSMaxRange(selRange)) && (caretPos < preeditText.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length-caretPos)]]; + if (_inlinePreedit) { + if ((caretPos >= end) && (caretPos < preeditText.length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length - caretPos)]]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length-(preeditText.length-caretPos)]; + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start, candidatePreviewText.length - (preeditText.length - end) - start) + caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (preeditText.length - caretPos)]; } else { - if ((NSMaxRange(selRange) < caretPos) && (caretPos > selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(caretPos-NSMaxRange(selRange)))]; - } else if ((NSMaxRange(selRange) < preeditText.length) && (caretPos <= selRange.location)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length-(preeditText.length-NSMaxRange(selRange)))]; + if ((end < caretPos) && (caretPos > start)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; + } else if ((end < preeditText.length) && (caretPos < end)) { + candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (preeditText.length - end))]; } - [self showPreeditString:candidatePreviewText selRange:NSMakeRange(selRange.location, candidatePreviewText.length-selRange.location) caretPos:candidatePreviewText.length]; + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) + caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; } } else { if (_inlinePreedit && !switcher) { - [self showPreeditString:preeditText selRange:selRange caretPos:caretPos]; + [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; } else { - NSRange empty = {0, 0}; // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:empty caretPos:0]; + [self showPreeditString:(preedit ? @"\0" : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } // update candidates @@ -631,12 +631,11 @@ -(void)rimeUpdate [candidates addObject:@(ctx.menu.candidates[i].text)]; if (ctx.menu.candidates[i].comment) { [comments addObject:@(ctx.menu.candidates[i].comment)]; - } - else { + } else { [comments addObject:@""]; } } - NSArray* labels; + NSArray *labels; if (ctx.menu.select_keys) { labels = @[@(ctx.menu.select_keys)]; } else if (ctx.select_labels) { @@ -650,7 +649,7 @@ -(void)rimeUpdate labels = @[]; } [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) - selRange:selRange + selRange:NSMakeRange(start, end - start) caretPos:switcher ? NSNotFound : caretPos candidates:candidates comments:comments From 05637f832e6f1775fcce94c340e5adfae20d3653 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 29 Jul 2023 18:18:14 +0200 Subject: [PATCH 121/164] try new icon --- .../RimeTray.symbolset/Contents.json | 22 +++ .../RimeTray.symbolset/rimeTray.fill.svg | 130 +++++++++++++++++ .../RimeTray.symbolset/rimeTray.svg | 136 ++++++++++++++++++ Squirrel.xcodeproj/project.pbxproj | 6 +- input_source.m | 20 ++- macos_keycode.m | 12 -- rime.pdf | Bin 324597 -> 49049 bytes rime.svg | 1 + rime_raw.pdf | Bin 0 -> 50581 bytes 9 files changed, 313 insertions(+), 14 deletions(-) create mode 100644 Assets.xcassets/RimeTray.symbolset/Contents.json create mode 100644 Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg create mode 100644 Assets.xcassets/RimeTray.symbolset/rimeTray.svg create mode 100644 rime.svg create mode 100644 rime_raw.pdf diff --git a/Assets.xcassets/RimeTray.symbolset/Contents.json b/Assets.xcassets/RimeTray.symbolset/Contents.json new file mode 100644 index 000000000..6bb99f0f4 --- /dev/null +++ b/Assets.xcassets/RimeTray.symbolset/Contents.json @@ -0,0 +1,22 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "rimeTray.svg", + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "rimeTray.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg b/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg new file mode 100644 index 000000000..2dff326b2 --- /dev/null +++ b/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg @@ -0,0 +1,130 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.4.0 + Requires Xcode 14 or greater + Generated from custom.123.rectangle.fill + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/RimeTray.symbolset/rimeTray.svg b/Assets.xcassets/RimeTray.symbolset/rimeTray.svg new file mode 100644 index 000000000..30df94e44 --- /dev/null +++ b/Assets.xcassets/RimeTray.symbolset/rimeTray.svg @@ -0,0 +1,136 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.4.0 + Requires Xcode 14 or greater + Generated from custom.123.rectangle + Typeset at 100 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index bebd9c005..bce2c2627 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 53; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -744,6 +744,8 @@ ); HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1 = "\"$(SRCROOT)/librime/src\""; HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/include\""; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSUIElement = YES; LD_RUNPATH_SEARCH_PATHS = ( "@loader_path/../Frameworks", "@loader_path/../Library/OpenSource", @@ -800,6 +802,8 @@ ); HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1 = "\"$(SRCROOT)/librime/src\""; HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_2 = "\"$(SRCROOT)/librime/include\""; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSUIElement = YES; LD_RUNPATH_SEARCH_PATHS = ( "@loader_path/../Frameworks\n\n@loader_path/../Library/OpenSource\n\n@loader_path/../Library/Frameworks", /usr/lib, diff --git a/input_source.m b/input_source.m index e649e5f62..38bcb6164 100644 --- a/input_source.m +++ b/input_source.m @@ -98,6 +98,24 @@ int GetEnabledInputModes(void) { } } CFRelease(sourceList); - NSLog(@"EnabledInputModes: %d", input_modes); + if (input_modes != 0) { + NSLog(@"EnabledInputModes: %d", input_modes); + } else { + NSArray *languages = [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK", @"yue"]]; + for (NSString *lang in languages) { + if ([lang isEqualToString:@"zh-Hans"]) + input_modes |= HANS_INPUT_MODE; + else if ([lang isEqualToString:@"zh-Hant"]) + input_modes |= HANT_INPUT_MODE; + else if ([lang isEqualToString:@"zh-HK"]) + input_modes |= CANT_INPUT_MODE; + } + if (input_modes != 0) { + NSLog(@"PreferredInputModes: %d", input_modes); + } else { + input_modes = HANS_INPUT_MODE; + NSLog(@"DefaultInputMode: %d", input_modes); + } + } return input_modes; } diff --git a/macos_keycode.m b/macos_keycode.m index 780a5be56..1e54ef106 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -131,18 +131,6 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) if (keychar >= 0x20 && keychar <= 0x7e) { return keychar; } -// else if (keychar == 0x1b) { // ^[ -// return XK_bracketleft; -// } -// else if (keychar == 0x1c) { // ^\ -// return XK_backslash; -// } -// else if (keychar == 0x1d) { // ^] -// return XK_bracketright; -// } -// else if (keychar == 0x1f) { // ^_ -// return XK_minus; -// } return XK_VoidSymbol; } diff --git a/rime.pdf b/rime.pdf index 8cac7ddaf2e0e9e72392329291c2616185e91ec7..37cb4fed29d2bf912ba10b1a2607e99de3e0b1f9 100644 GIT binary patch delta 35226 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%0DdjRIA127oUX#2Ncmh#Jdd}*(GobN6!?(jnkQ}QD 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 literal 324597 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�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 \ No newline at end of file diff --git a/rime_raw.pdf b/rime_raw.pdf new file mode 100644 index 0000000000000000000000000000000000000000..423b63726fec2106e74d40645d2c16bcadde862a GIT binary patch literal 50581 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 Date: Sat, 5 Aug 2023 05:21:05 +0200 Subject: [PATCH 122/164] format --- SquirrelApplicationDelegate.h | 20 ++++---- SquirrelApplicationDelegate.m | 86 +++++++++++++++++------------------ SquirrelConfig.m | 11 +++-- SquirrelInputController.m | 26 ++++++----- SquirrelPanel.h | 36 +++++++-------- input_source.m | 26 ++++++----- main.m | 16 +++---- 7 files changed, 111 insertions(+), 110 deletions(-) diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index 4f3e421d6..783811dff 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -13,17 +13,17 @@ @property(nonatomic, readonly, strong) SquirrelConfig *config; @property(nonatomic, readonly) BOOL enableNotifications; --(IBAction)deploy:(id)sender; --(IBAction)syncUserData:(id)sender; --(IBAction)configure:(id)sender; --(IBAction)openWiki:(id)sender; +- (IBAction)deploy:(id)sender; +- (IBAction)syncUserData:(id)sender; +- (IBAction)configure:(id)sender; +- (IBAction)openWiki:(id)sender; --(void)setupRime; --(void)startRimeWithFullCheck:(BOOL)fullCheck; --(void)loadSettings; --(void)loadSchemaSpecificSettings:(NSString *)schemaId; +- (void)setupRime; +- (void)startRimeWithFullCheck:(BOOL)fullCheck; +- (void)loadSettings; +- (void)loadSchemaSpecificSettings:(NSString *)schemaId; -@property (nonatomic, readonly) BOOL problematicLaunchDetected; +@property(nonatomic, readonly) BOOL problematicLaunchDetected; @end @@ -34,4 +34,4 @@ @end // also used in main.m -extern void show_message(const char* msg_text, const char* msg_id); +extern void show_message(const char *msg_text, const char *msg_id); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 10c1688e2..82350ad3e 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -8,7 +8,7 @@ @implementation SquirrelApplicationDelegate --(IBAction)deploy:(id)sender +- (IBAction)deploy:(id)sender { NSLog(@"Start maintenance..."); [self shutdownRime]; @@ -16,23 +16,23 @@ -(IBAction)deploy:(id)sender [self loadSettings]; } --(IBAction)syncUserData:(id)sender +- (IBAction)syncUserData:(id)sender { NSLog(@"Sync user data"); rime_get_api()->sync_user_data(); } --(IBAction)configure:(id)sender +- (IBAction)configure:(id)sender { [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[@"file://" stringByAppendingString:(@"~/Library/Rime").stringByStandardizingPath]]]; } --(IBAction)openWiki:(id)sender +- (IBAction)openWiki:(id)sender { [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:kRimeWikiURL]]; } -void show_message(const char* msg_text, const char* msg_id) { +void show_message(const char *msg_text, const char *msg_id) { @autoreleasepool { id notification = [[NSClassFromString(@"NSUserNotification") alloc] init]; [notification performSelector:@selector(setTitle:) @@ -40,32 +40,31 @@ void show_message(const char* msg_text, const char* msg_id) { [notification performSelector:@selector(setSubtitle:) withObject:NSLocalizedString(@(msg_text), nil)]; id notificationCenter = [(id)NSClassFromString(@"NSUserNotificationCenter") - performSelector:@selector(defaultUserNotificationCenter)]; + performSelector:@selector(defaultUserNotificationCenter)]; [notificationCenter performSelector:@selector(removeAllDeliveredNotifications)]; [notificationCenter performSelector:@selector(deliverNotification:) withObject:notification]; } } -static void show_status_message(const char *msg_text_long, const char *msg_text_short, const char* msg_id) { - SquirrelPanel* panel = NSApp.squirrelAppDelegate.panel; +static void show_status_message(const char *msg_text_long, const char *msg_text_short, const char *msg_id) { + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; + if (panel) { NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; NSString *msgShort = msg_text_short ? @(msg_text_short) : nil; - [panel updateStatusLong: msgLong statusShort: msgShort]; + [panel updateStatusLong:msgLong statusShort:msgShort]; } } -void notification_handler(void* context_object, RimeSessionId session_id, - const char* message_type, const char* message_value) { +void notification_handler(void *context_object, RimeSessionId session_id, + const char *message_type, const char *message_value) { if (!strcmp(message_type, "deploy")) { if (!strcmp(message_value, "start")) { show_message("deploy_start", message_type); - } - else if (!strcmp(message_value, "success")) { + } else if (!strcmp(message_value, "success")) { show_message("deploy_success", message_type); - } - else if (!strcmp(message_value, "failure")) { + } else if (!strcmp(message_value, "failure")) { show_message("deploy_failure", message_type); } return; @@ -77,7 +76,7 @@ void notification_handler(void* context_object, RimeSessionId session_id, } // schema change if (!strcmp(message_type, "schema")) { - const char* schema_name = strchr(message_value, '/'); + const char *schema_name = strchr(message_value, '/'); if (schema_name) { ++schema_name; show_status_message(schema_name, schema_name, message_type); @@ -87,10 +86,9 @@ void notification_handler(void* context_object, RimeSessionId session_id, // option change if (!strcmp(message_type, "option")) { Bool state = message_value[0] != '!'; - const char* option_name = message_value + !state; + const char *option_name = message_value + !state; struct rime_string_slice_t state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); struct rime_string_slice_t state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); - if (state_label_long.str || state_label_short.str) { const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; show_status_message(state_label_long.str, short_message, message_type); @@ -98,10 +96,10 @@ void notification_handler(void* context_object, RimeSessionId session_id, } } --(void)setupRime +- (void)setupRime { - NSString* userDataDir = (@"~/Library/Rime").stringByStandardizingPath; - NSFileManager* fileManager = [NSFileManager defaultManager]; + NSString *userDataDir = (@"~/Library/Rime").stringByStandardizingPath; + NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:userDataDir]) { if (![fileManager createDirectoryAtPath:userDataDir withIntermediateDirectories:YES @@ -117,12 +115,12 @@ -(void)setupRime squirrel_traits.distribution_code_name = "Squirrel"; squirrel_traits.distribution_name = "鼠鬚管"; squirrel_traits.distribution_version = - [[NSBundle mainBundle].infoDictionary[@"CFBundleVersion"] UTF8String]; + [[NSBundle mainBundle].infoDictionary[@"CFBundleVersion"] UTF8String]; squirrel_traits.app_name = "rime.squirrel"; rime_get_api()->setup(&squirrel_traits); } --(void)startRimeWithFullCheck:(BOOL)fullCheck +- (void)startRimeWithFullCheck:(BOOL)fullCheck { NSLog(@"Initializing la rime..."); rime_get_api()->initialize(NULL); @@ -138,21 +136,21 @@ - (void)shutdownRime { rime_get_api()->finalize(); } --(void)loadSettings { +- (void)loadSettings { _config = [[SquirrelConfig alloc] init]; if (![_config openBaseConfig]) { return; } _enableNotifications = - ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; + ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; [self.panel loadConfig:_config forDarkMode:NO]; if (@available(macOS 10.14, *)) { [self.panel loadConfig:_config forDarkMode:YES]; } } --(void)loadSchemaSpecificSettings:(NSString *)schemaId { +- (void)loadSchemaSpecificSettings:(NSString *)schemaId { if (schemaId.length == 0 || [schemaId characterAtIndex:0] == '.') { return; } @@ -164,7 +162,7 @@ -(void)loadSchemaSpecificSettings:(NSString *)schemaId { [self.panel loadConfig:self.config forDarkMode:NO]; } if (@available(macOS 10.14, *)) { - if ([schema openWithSchemaId:schemaId baseConfig:self.config] && + if ([schema openWithSchemaId:schemaId baseConfig:self.config] && [schema hasSection:@"style"]) { [self.panel loadConfig:schema forDarkMode:YES]; } else { @@ -175,17 +173,17 @@ -(void)loadSchemaSpecificSettings:(NSString *)schemaId { } // prevent freezing the system --(BOOL)problematicLaunchDetected +- (BOOL)problematicLaunchDetected { BOOL detected = NO; - NSString* logfile = - [NSTemporaryDirectory() stringByAppendingPathComponent:@"squirrel_launch.dat"]; + NSString *logfile = + [NSTemporaryDirectory() stringByAppendingPathComponent:@"squirrel_launch.dat"]; //NSLog(@"[DEBUG] archive: %@", logfile); - NSData* archive = [NSData dataWithContentsOfFile:logfile + NSData *archive = [NSData dataWithContentsOfFile:logfile options:NSDataReadingUncached error:nil]; if (archive) { - NSDate* previousLaunch; + NSDate *previousLaunch; if (@available(macOS 10.13, *)) { previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; } else { @@ -195,8 +193,8 @@ -(BOOL)problematicLaunchDetected detected = YES; } } - NSDate* now = [NSDate date]; - NSData* record; + NSDate *now = [NSDate date]; + NSData *record; if (@available(macOS 10.13, *)) { record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; } else { @@ -206,25 +204,25 @@ -(BOOL)problematicLaunchDetected return detected; } --(void)workspaceWillPowerOff:(NSNotification *)aNotification +- (void)workspaceWillPowerOff:(NSNotification *)aNotification { NSLog(@"Finalizing before logging out."); [self shutdownRime]; } --(void)rimeNeedsReload:(NSNotification *)aNotification +- (void)rimeNeedsReload:(NSNotification *)aNotification { NSLog(@"Reloading rime on demand."); [self deploy:nil]; } --(void)rimeNeedsSync:(NSNotification *)aNotification +- (void)rimeNeedsSync:(NSNotification *)aNotification { NSLog(@"Sync rime on demand."); [self syncUserData:nil]; } --(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { NSLog(@"Squirrel is quitting."); rime_get_api()->cleanup_all_sessions(); @@ -234,15 +232,15 @@ -(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender //add an awakeFromNib item so that we can set the action method. Note that //any menuItems without an action will be disabled when displayed in the Text //Input Menu. --(void)awakeFromNib +- (void)awakeFromNib { - NSNotificationCenter* center = [NSWorkspace sharedWorkspace].notificationCenter; + NSNotificationCenter *center = [NSWorkspace sharedWorkspace].notificationCenter; [center addObserver:self selector:@selector(workspaceWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:nil]; - NSDistributedNotificationCenter* notifCenter = [NSDistributedNotificationCenter defaultCenter]; + NSDistributedNotificationCenter *notifCenter = [NSDistributedNotificationCenter defaultCenter]; [notifCenter addObserver:self selector:@selector(rimeNeedsReload:) name:@"SquirrelReloadNotification" @@ -252,10 +250,9 @@ -(void)awakeFromNib selector:@selector(rimeNeedsSync:) name:@"SquirrelSyncNotification" object:nil]; - } --(void)dealloc +- (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; @@ -268,9 +265,8 @@ -(void)dealloc @implementation NSApplication (SquirrelApp) --(SquirrelApplicationDelegate *)squirrelAppDelegate { +- (SquirrelApplicationDelegate *)squirrelAppDelegate { return (SquirrelApplicationDelegate *)self.delegate; } @end - diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 1a7e81c20..9d804b3f8 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -76,7 +76,7 @@ - (double)getDouble:(NSString *)option { } - (NSNumber *)getOptionalBool:(NSString *)option { - NSNumber* cachedValue = [self cachedValueOfClass:[NSNumber class] forKey:option]; + NSNumber *cachedValue = [self cachedValueOfClass:[NSNumber class] forKey:option]; if (cachedValue) { return cachedValue; } @@ -117,7 +117,7 @@ - (NSString *)getString:(NSString *)option { return cachedValue; } const char *value = - _isOpen ? rime_get_api()->config_get_cstring(&_config, option.UTF8String) : NULL; + _isOpen ? rime_get_api()->config_get_cstring(&_config, option.UTF8String) : NULL; if (value) { return _cache[option] = @(value); } @@ -151,8 +151,8 @@ - (NSColor *)getPattern:(NSString *)option { } - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { - NSString * rootKey = [@"app_options/" stringByAppendingString:appName]; - SquirrelMutableAppOptions* appOptions = [[SquirrelMutableAppOptions alloc] init]; + NSString *rootKey = [@"app_options/" stringByAppendingString:appName]; + SquirrelMutableAppOptions *appOptions = [[SquirrelMutableAppOptions alloc] init]; RimeConfigIterator iterator; rime_get_api()->config_begin_map(&iterator, &_config, rootKey.UTF8String); while (rime_get_api()->config_next(&iterator)) { @@ -204,7 +204,7 @@ - (NSColor *)patternFromFile:(NSString *)filePath { if (filePath == nil) { return nil; } - NSFileManager* fileManager = [NSFileManager defaultManager]; + NSFileManager *fileManager = [NSFileManager defaultManager]; [fileManager changeCurrentDirectoryPath:[@"~/Library/Rime" stringByStandardizingPath]]; NSString *patternFile = [filePath stringByStandardizingPath]; if ([fileManager fileExistsAtPath:patternFile]) { @@ -213,4 +213,5 @@ - (NSColor *)patternFromFile:(NSString *)filePath { } return nil; } + @end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index f220fa938..bb9660be0 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -41,9 +41,9 @@ @implementation SquirrelInputController { } /*! - @method - @abstract Receive incoming event - @discussion This method receives key events from the client application. + @method + @abstract Receive incoming event + @discussion This method receives key events from the client application. */ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender { @@ -127,8 +127,9 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. - if (modifiers & OSX_COMMAND_MASK) + if (modifiers & OSX_COMMAND_MASK) { break; + } int keyCode = event.keyCode; NSString *keyChars = ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && @@ -194,10 +195,10 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers // Simulate key-ups for every interesting key-down for chord-typing. if (handled) { BOOL is_chording_key = - (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || - rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || + rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || + rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; @@ -254,8 +255,9 @@ - (void)updateChord:(int)keycode modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); for (int i = 0; i < _chordKeyCount; ++i) { - if (_chordKeyCodes[i] == keycode) + if (_chordKeyCodes[i] == keycode) { return; + } } if (_chordKeyCount >= N_KEY_ROLL_OVER) { // you are cheating. only one human typist (fingers <= 10) is supported. @@ -620,7 +622,7 @@ - (void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @"\0" : @"") selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } // update candidates @@ -641,7 +643,7 @@ - (void)rimeUpdate } else if (ctx.select_labels) { NSMutableArray *selectLabels = [NSMutableArray array]; for (i = 0; i < ctx.menu.page_size; ++i) { - char* label_str = ctx.select_labels[i]; + char *label_str = ctx.select_labels[i]; [selectLabels addObject:@(label_str)]; } labels = selectLabels; @@ -650,7 +652,7 @@ - (void)rimeUpdate } [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) selRange:NSMakeRange(start, end - start) - caretPos:switcher ? NSNotFound : caretPos + caretPos:(switcher ? NSNotFound : caretPos) candidates:candidates comments:comments labels:labels diff --git a/SquirrelPanel.h b/SquirrelPanel.h index ea64327dc..f50268390 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -18,23 +18,23 @@ @property(nonatomic, assign) SquirrelInputController *inputController; --(void)showPreedit:(NSString*)preedit - selRange:(NSRange)selRange - caretPos:(NSUInteger)caretPos - candidates:(NSArray*)candidates - comments:(NSArray*)comments - labels:(NSArray*)labels - highlighted:(NSUInteger)index - pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage - turnPage:(NSUInteger)turnPage - update:(BOOL)update; - --(void)hide; - --(void)updateStatusLong:(NSString*)messageLong statusShort:(NSString*)messageShort; - --(void)loadConfig:(SquirrelConfig*)config - forDarkMode:(BOOL)isDark; +- (void)showPreedit:(NSString *)preedit + selRange:(NSRange)selRange + caretPos:(NSUInteger)caretPos + candidates:(NSArray *)candidates + comments:(NSArray *)comments + labels:(NSArray *)labels + highlighted:(NSUInteger)index + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage + turnPage:(NSUInteger)turnPage + update:(BOOL)update; + +- (void)hide; + +- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort; + +- (void)loadConfig:(SquirrelConfig *)config + forDarkMode:(BOOL)isDark; @end diff --git a/input_source.m b/input_source.m index 38bcb6164..269ba3933 100644 --- a/input_source.m +++ b/input_source.m @@ -1,14 +1,14 @@ #import static const unsigned char kInstallLocation[] = - "/Library/Input Methods/Squirrel.app"; + "/Library/Input Methods/Squirrel.app"; static NSString *const kHansInputModeID = - @"im.rime.inputmethod.Squirrel.Hans"; + @"im.rime.inputmethod.Squirrel.Hans"; static NSString *const kHantInputModeID = - @"im.rime.inputmethod.Squirrel.Hant"; + @"im.rime.inputmethod.Squirrel.Hant"; static NSString *const kCantInputModeID = - @"im.rime.inputmethod.Squirrel.Cant"; + @"im.rime.inputmethod.Squirrel.Cant"; #define HANS_INPUT_MODE (1 << 0) #define HANT_INPUT_MODE (1 << 1) @@ -16,7 +16,7 @@ void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( - NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); + NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); if (installedLocationURL) { TISRegisterInputSource(installedLocationURL); CFRelease(installedLocationURL); @@ -41,7 +41,7 @@ void ActivateInputSource(int enabled_modes) { TISEnableInputSource(inputSource); NSLog(@"Enabled input source: %@", sourceID); CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceIsSelectCapable); + inputSource, kTISPropertyInputSourceIsSelectCapable); if (CFBooleanGetValue(isSelectable)) { TISSelectInputSource(inputSource); NSLog(@"Selected input source: %@", sourceID); @@ -88,12 +88,13 @@ int GetEnabledInputModes(void) { CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceIsEnabled)); if (CFBooleanGetValue(isEnabled)) { - if ([sourceID isEqualToString:kHansInputModeID]) + if ([sourceID isEqualToString:kHansInputModeID]) { input_modes |= HANS_INPUT_MODE; - else if ([sourceID isEqualToString:kHantInputModeID]) + } else if ([sourceID isEqualToString:kHantInputModeID]) { input_modes |= HANT_INPUT_MODE; - else if ([sourceID isEqualToString:kCantInputModeID]) + } else if ([sourceID isEqualToString:kCantInputModeID]) { input_modes |= CANT_INPUT_MODE; + } } } } @@ -103,12 +104,13 @@ int GetEnabledInputModes(void) { } else { NSArray *languages = [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK", @"yue"]]; for (NSString *lang in languages) { - if ([lang isEqualToString:@"zh-Hans"]) + if ([lang isEqualToString:@"zh-Hans"]) { input_modes |= HANS_INPUT_MODE; - else if ([lang isEqualToString:@"zh-Hant"]) + } else if ([lang isEqualToString:@"zh-Hant"]) { input_modes |= HANT_INPUT_MODE; - else if ([lang isEqualToString:@"zh-HK"]) + } else if ([lang isEqualToString:@"zh-HK"]) { input_modes |= CANT_INPUT_MODE; + } } if (input_modes != 0) { NSLog(@"PreferredInputModes: %d", input_modes); diff --git a/main.m b/main.m index 2a1a556f3..8feae1ce3 100644 --- a/main.m +++ b/main.m @@ -20,7 +20,7 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--quit", argv[1])) { NSString *bundleId = [NSBundle mainBundle].bundleIdentifier; NSArray *runningSquirrels = - [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId]; + [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId]; for (NSRunningApplication *squirrelApp in runningSquirrels) { [squirrelApp terminate]; } @@ -29,8 +29,8 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--reload", argv[1])) { [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"SquirrelReloadNotification" - object:nil]; + postNotificationName:@"SquirrelReloadNotification" + object:nil]; return 0; } @@ -56,8 +56,8 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--sync", argv[1])) { [[NSDistributedNotificationCenter defaultCenter] - postNotificationName:@"SquirrelSyncNotification" - object:nil]; + postNotificationName:@"SquirrelSyncNotification" + object:nil]; return 0; } @@ -65,8 +65,8 @@ int main(int argc, char *argv[]) { // find the bundle identifier and then initialize the input method server NSBundle *main = [NSBundle mainBundle]; IMKServer *server __unused = - [[IMKServer alloc] initWithName:(NSString *)kConnectionName - bundleIdentifier:main.bundleIdentifier]; + [[IMKServer alloc] initWithName:(NSString *)kConnectionName + bundleIdentifier:main.bundleIdentifier]; // load the bundle explicitly because in this case the input method is a // background only application @@ -74,7 +74,7 @@ int main(int argc, char *argv[]) { // opencc will be configured with relative dictionary paths [[NSFileManager defaultManager] - changeCurrentDirectoryPath:main.sharedSupportPath]; + changeCurrentDirectoryPath:main.sharedSupportPath]; if (NSApp.squirrelAppDelegate.problematicLaunchDetected) { NSLog(@"Problematic launch detected!"); From ce4c14c4ae737c33f588438fe65a5cc762020af0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 5 Aug 2023 09:06:48 +0200 Subject: [PATCH 123/164] macOS 14 compatible --- SquirrelPanel.m | 246 +++++++++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 118 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 86c6e1395..66076f16b 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -261,6 +261,7 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @interface SquirrelView : NSView @property(nonatomic, readonly) NSTextView *textView; +@property(nonatomic, readonly) NSTextStorage *textStorage; @property(nonatomic, readonly) NSEdgeInsets insets; @property(nonatomic, readonly) NSArray *candidateRanges; @property(nonatomic, readonly) NSUInteger highlightedIndex; @@ -288,7 +289,7 @@ - (void) drawViewWithInsets:(NSEdgeInsets)insets pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; - (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPoint *)origin + atOrigin:(NSPointPointer)origin withReferenceFont:(NSFont *)refFont paragraphStyle:(NSParagraphStyle *)style; @@ -340,10 +341,11 @@ - (instancetype)initWithFrame:(NSRect)frameRect { NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; _layoutManager.textContainer = textContainer; - NSTextContentStorage *textStorage = [[NSTextContentStorage alloc] init]; - [textStorage addTextLayoutManager:_layoutManager]; + NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; + [contentStorage addTextLayoutManager:_layoutManager]; _textView = [[NSTextView alloc] initWithFrame:frameRect textContainer:_layoutManager.textContainer]; + _textStorage = _textView.textContentStorage.textStorage; } else { NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; layoutManager.backgroundLayoutEnabled = YES; @@ -352,8 +354,8 @@ - (instancetype)initWithFrame:(NSRect)frameRect { NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; [layoutManager addTextContainer:textContainer]; - NSTextStorage *textStorage = [[NSTextStorage alloc] init]; - [textStorage addLayoutManager:layoutManager]; + _textStorage = [[NSTextStorage alloc] init]; + [_textStorage addLayoutManager:layoutManager]; _textView = [[NSTextView alloc] initWithFrame:frameRect textContainer:textContainer]; } @@ -366,6 +368,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; + _textView.usesAdaptiveColorMappingForDarkAppearance = YES; } return self; } @@ -374,22 +377,19 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) if (range.location == NSNotFound) { return nil; } else { - NSTextContentManager *contentManager = _layoutManager.textContentManager; - id startLocation = [contentManager locationFromLocation:contentManager.documentRange.location withOffset:range.location]; - id endLocation = [contentManager locationFromLocation:startLocation withOffset:range.length]; + NSTextContentStorage *contentStorage = _textView.textContentStorage; + id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location withOffset:range.location]; + id endLocation = [contentStorage locationFromLocation:startLocation withOffset:range.length]; return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; } } - (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPoint *)origin + atOrigin:(NSPointPointer)origin withReferenceFont:(NSFont *)refFont paragraphStyle:(NSParagraphStyle *)style { NSLayoutManager *layoutManager = _textView.layoutManager; NSTextContainer *textContainer = _textView.textContainer; - NSTextStorage *textStorage = _textView.textStorage; - - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; CGFloat refFontHeight = refFont.ascender - refFont.descender; CGFloat refBaseline = refFont.ascender; @@ -397,6 +397,7 @@ - (NSRect)setLineRectForRange:(NSRange)charRange lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; NSRect blockRect = NSZeroRect; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSUInteger i = glyphRange.location; NSRange lineRange = NSMakeRange(i, 0); while (i < NSMaxRange(glyphRange)) { @@ -414,17 +415,18 @@ - (NSRect)setLineRectForRange:(NSRange)charRange usedRect.size.height = MAX(NSHeight(usedRect), lineHeight + style.lineSpacing); alignment += style.lineSpacing / 2; } - if (style.paragraphSpacing > 0 && NSMaxRange(lineCharRange) != textStorage.length && - [textStorage.string characterAtIndex:NSMaxRange(lineCharRange) - 1] == '\n') { + if (style.paragraphSpacing > 0 && NSMaxRange(lineCharRange) != _textStorage.length && + [_textStorage.string characterAtIndex:NSMaxRange(lineCharRange) - 1] == '\n') { rect.size.height += style.paragraphSpacing; } if (style.paragraphSpacingBefore > 0 && lineCharRange.location != 0 && - [textStorage.string characterAtIndex:lineCharRange.location - 1] == '\n') { + [_textStorage.string characterAtIndex:lineCharRange.location - 1] == '\n') { rect.size.height += style.paragraphSpacingBefore; usedRect.origin.y += style.paragraphSpacingBefore; alignment += style.paragraphSpacingBefore; } - [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:NSIntersectionRect(usedRect, rect)]; + usedRect = NSIntersectionRect(usedRect, rect); + [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:usedRect]; blockRect = NSUnionRect(blockRect, rect); origin->y = NSMaxY(rect); @@ -434,11 +436,12 @@ - (NSRect)setLineRectForRange:(NSRange)charRange while (j < NSMaxRange(lineRange)) { NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; - NSFont *runFont = [textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; + NSFont *runFont = [_textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; - CGFloat baselineOffset = [[textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL] doubleValue]; - NSRange fontRunGlyphRange = [layoutManager characterRangeForGlyphRange:fontRunRange actualGlyphRange:NULL]; - NSRange runRange = NSIntersectionRange(fontRunGlyphRange, [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + NSNumber *baselineOffset = [_textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL]; + CGFloat offset = (baselineOffset == nil) ? 0.0 : baselineOffset.doubleValue; + NSRange fontRunGlyphRange = [layoutManager glyphRangeForCharacterRange:fontRunRange actualCharacterRange:NULL]; + NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange)-j), [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { runFont = runFont.verticalFont; resizedRefFont = resizedRefFont.verticalFont; @@ -447,14 +450,14 @@ - (NSRect)setLineRectForRange:(NSRange)charRange CGFloat runFontHeight = runFont.ascender - runFont.descender; CGFloat resizedRefFontHeight = resizedRefFont.ascender - resizedRefFont.descender; if (verticalLayout) { - runGlyphPosition.y = alignment - baselineOffset + ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/4); + runGlyphPosition.y = alignment - offset + ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/4); if (runFont.isVertical) { runGlyphPosition.x += ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/2); } else { runGlyphPosition.y += runBaseline - runFontHeight/2; } } else { - runGlyphPosition.y = alignment - baselineOffset; + runGlyphPosition.y = alignment - offset; } [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); @@ -482,26 +485,34 @@ - (NSRect)contentRectForRange:(NSRange)range { NSTextRange *textRange = [self getTextRangeFromRange:range]; __block NSRect contentRect = NSZeroRect; [_layoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard + type:NSTextLayoutManagerSegmentTypeHighlight options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { contentRect = NSUnionRect(contentRect, segmentRect); return YES; }]; return contentRect; } else { - NSRange glyphRange = [_textView.layoutManager glyphRangeForCharacterRange:range - actualCharacterRange:NULL]; - NSRect rect = [_textView.layoutManager boundingRectForGlyphRange:glyphRange - inTextContainer:_textView.textContainer]; - NSRect firstLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location - effectiveRange:NULL]; - NSRect finalLineRect = [_textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 - effectiveRange:NULL]; - NSRect contentRect = NSMakeRect(NSMinX(rect), NSMinY(firstLineRect), - NSWidth(rect), NSMaxY(finalLineRect) - NSMinY(firstLineRect)); - return contentRect; + NSTextContainer *textContainer = _textView.textContainer; + NSLayoutManager *layoutManager = _textView.layoutManager; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:range + actualCharacterRange:NULL]; + NSRange firstLineRange = NSMakeRange(NSNotFound, 0); + NSRect firstLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location + effectiveRange:&firstLineRange]; + if (NSMaxRange(glyphRange) <= NSMaxRange(firstLineRange)) { + CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(firstLineRect) - textContainer.lineFragmentPadding; + return NSMakeRect(NSMinX(firstLineRect) + startX, NSMinY(firstLineRect), + endX - startX, NSHeight(firstLineRect)); + } else { + NSRect finalLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 + effectiveRange:NULL]; + return NSMakeRect(textContainer.lineFragmentPadding, NSMinY(firstLineRect), + textContainer.size.width - textContainer.lineFragmentPadding * 2, + NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + } } } @@ -583,40 +594,48 @@ BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is the complete line fragment in the middle if the range spans no less than one full line -- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRect bodyRect:(NSRect *)bodyRect trailingRect:(NSRect *)trailingRect { +- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)leadingRect bodyRect:(NSRectPointer)bodyRect trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:charRange]; - NSMutableArray *lineRects = [[NSMutableArray alloc] init]; + NSMutableArray *lineRects = [NSMutableArray arrayWithCapacity:2]; + NSMutableArray *lineRanges = [NSMutableArray arrayWithCapacity:2]; [_layoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baseline, NSTextContainer *textContainer) { + ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { if (!nearEmptyRect(segmentRect)) { [lineRects addObject:[NSValue valueWithRect:segmentRect]]; + [lineRanges addObject:segmentRange]; } return YES; }]; if (lineRects.count == 1) { *bodyRect = lineRects[0].rectValue; } else { - NSRect firstLineRect = lineRects.firstObject.rectValue; - NSRect lastLineRect = lineRects.lastObject.rectValue; - if (NSMaxX(lastLineRect) == NSMaxX(firstLineRect)) { - if (NSMinX(firstLineRect) == NSMinX(lastLineRect)) { - *bodyRect = NSUnionRect(firstLineRect, lastLineRect); + CGFloat rightEdge = _textView.textContainer.size.width - _textView.textContainer.lineFragmentPadding; + NSRect leadingLineRect = lineRects.firstObject.rectValue; + leadingLineRect.size.width = rightEdge - NSMinX(leadingLineRect); + NSRect trailingLineRect = lineRects.lastObject.rectValue; + CGFloat leftEdge = _textView.textContainer.lineFragmentPadding; + if (NSMaxX(trailingLineRect) == NSMaxX(leadingLineRect)) { + if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { + *bodyRect = NSUnionRect(leadingLineRect, trailingLineRect); } else { - *leadingRect = firstLineRect; - *bodyRect = NSUnionRect(lineRects[1].rectValue, lastLineRect); + *leadingRect = leadingLineRect; + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { - *trailingRect = lastLineRect; - if (NSMinX(firstLineRect) == NSMinX(lastLineRect)) { - *bodyRect = NSUnionRect(firstLineRect, lineRects[lineRects.count - 2].rectValue); + *trailingRect = trailingLineRect; + if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { + *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = firstLineRect; - if (lineRects.count > 2) { - *bodyRect = NSUnionRect(lineRects[1].rectValue, lineRects[lineRects.count - 2].rectValue); + *leadingRect = leadingLineRect; + if (lineRanges.lastObject.location > lineRanges.firstObject.endLocation) { + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } } @@ -626,47 +645,43 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRect *)leadingRe NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; - CGFloat endX = NSMaxX([layoutManager boundingRectForGlyphRange:NSMakeRange(NSMaxRange(glyphRange) - 1, 1) - inTextContainer:textContainer]); - NSRect boundingRect = [layoutManager boundingRectForGlyphRange:glyphRange - inTextContainer:textContainer]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location effectiveRange:&leadingLineRange]; + CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - *bodyRect = NSMakeRect(NSMinX(leadingLineRect) + startX, - NSMinY(leadingLineRect), + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect) - textContainer.lineFragmentPadding; + *bodyRect = NSMakeRect(startX, NSMinY(leadingLineRect), endX - startX, NSHeight(leadingLineRect)); } else { - CGFloat rightEdge = MAX(NSMaxX(leadingLineRect) - textContainer.lineFragmentPadding, NSMaxX(boundingRect)); + CGFloat rightEdge = textContainer.size.width - textContainer.lineFragmentPadding; NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 effectiveRange:&trailingLineRange]; - CGFloat leftEdge = MIN(NSMinX(trailingLineRect) + textContainer.lineFragmentPadding, NSMinX(boundingRect)); + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(trailingLineRect) - textContainer.lineFragmentPadding; + CGFloat leftEdge = textContainer.lineFragmentPadding; if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), - rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX, NSMinY(leadingLineRect), - rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), - rightEdge - leftEdge, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); + *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), + rightEdge - startX, NSHeight(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { *trailingRect = NSMakeRect(leftEdge, NSMinY(trailingLineRect), - NSMinX(trailingLineRect) + endX - leftEdge, NSHeight(trailingLineRect)); + endX - leftEdge, NSHeight(trailingLineRect)); if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), - rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(NSMinX(leadingLineRect) + startX, - NSMinY(leadingLineRect), - rightEdge - NSMinX(leadingLineRect) - startX, NSHeight(leadingLineRect)); + *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), + rightEdge - startX, NSHeight(leadingLineRect)); if (trailingLineRange.location > NSMaxRange(leadingLineRange)) { - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), - rightEdge - leftEdge, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); + *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } } @@ -747,13 +762,12 @@ - (void)updateLayer { NSRange visibleRange; if (@available(macOS 12.0, *)) { - visibleRange = NSMakeRange(0, _textView.textContentStorage.textStorage.length); + visibleRange = NSMakeRange(0, _textStorage.length); } else { NSRange containerGlyphRange = {NSNotFound, 0}; [_textView.layoutManager textContainerForGlyphAtIndex:0 effectiveRange:&containerGlyphRange]; visibleRange = [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange actualGlyphRange:NULL]; } - NSRange preeditRange = NSIntersectionRange(_preeditRange, visibleRange); NSRange candidateBlockRange = NSIntersectionRange(NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue), visibleRange); NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange); @@ -761,7 +775,6 @@ - (void)updateLayer { NSRect preeditRect = NSZeroRect; NSRect candidateBlockRect = NSZeroRect; NSRect pagingLineRect = NSZeroRect; - NSRect statusRect = NSZeroRect; if (@available(macOS 12.0, *)) { if (preeditRange.length > 0) { preeditRect = [self contentRectForRange:preeditRange]; @@ -770,12 +783,12 @@ - (void)updateLayer { } } if (candidateBlockRange.length > 0) { - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace / 2); + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); if (preeditRange.length == 0) { - candidateBlockRect.origin.y += theme.linespace / 2; + candidateBlockRect.origin.y += theme.linespace/2; } } else if (preeditRange.length == 0) { // status message - statusRect = [self contentRectForRange:visibleRange]; + candidateBlockRect = [self contentRectForRange:visibleRange]; } if (!theme.linear && pagingRange.length > 0) { pagingLineRect = [self contentRectForRange:pagingRange]; @@ -784,34 +797,33 @@ - (void)updateLayer { } } else { NSPoint lineOrigin = NSZeroPoint; + CGFloat refFontSize = _preeditRange.length == 0 && _candidateRanges.count == 0 ? [theme.commentAttrs[NSFontAttributeName] pointSize] : [theme.attrs[NSFontAttributeName] pointSize]; + NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, refFontSize, (CFStringRef)@"zh")); + if (theme.vertical) { + refFont = refFont.verticalFont; + } if (preeditRange.length > 0) { - NSFont *preeditRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.preeditAttrs[NSFontAttributeName] pointSize], (CFStringRef)@"zh")); preeditRect = [self setLineRectForRange:preeditRange atOrigin:&lineOrigin - withReferenceFont:(theme.vertical ? preeditRefFont.verticalFont : preeditRefFont) + withReferenceFont:refFont paragraphStyle:theme.preeditParagraphStyle]; } if (candidateBlockRange.length > 0) { - CGFloat fontSize = MAX([theme.attrs[NSFontAttributeName] pointSize], - MAX([theme.commentAttrs[NSFontAttributeName] pointSize], - [theme.labelAttrs[NSFontAttributeName] pointSize])); - NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, fontSize, (CFStringRef)@"zh")); candidateBlockRect = [self setLineRectForRange:candidateBlockRange atOrigin:&lineOrigin - withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) + withReferenceFont:refFont paragraphStyle:theme.paragraphStyle]; if (preeditRange.length == 0) { - candidateBlockRect.size.height += theme.linespace / 2; + candidateBlockRect.size.height += theme.linespace/2; } if (theme.linear || pagingRange.length == 0) { - candidateBlockRect.size.height += theme.linespace / 2; + candidateBlockRect.size.height += theme.linespace/2; } } else if (preeditRange.length == 0) { // status message - NSFont *statusRefFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, [theme.commentAttrs[NSFontAttributeName] pointSize], (CFStringRef)@"zh")); - statusRect = [self setLineRectForRange:visibleRange - atOrigin:&lineOrigin - withReferenceFont:(theme.vertical ? statusRefFont.verticalFont : statusRefFont) - paragraphStyle:theme.statusParagraphStyle]; + candidateBlockRect = [self setLineRectForRange:visibleRange + atOrigin:&lineOrigin + withReferenceFont:refFont + paragraphStyle:theme.statusParagraphStyle]; } if (!theme.linear && pagingRange.length > 0) { pagingLineRect = [self setLineRectForRange:pagingRange @@ -984,7 +996,7 @@ - (void)updateLayer { textContainerLayer.path = [textContainerPath quartzPath]; textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3); - [self.layer setSublayers:NULL]; + [self.layer setSublayers:nil]; self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); if (theme.backgroundImage) { CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; @@ -1099,7 +1111,7 @@ @implementation SquirrelPanel { NSScreen *_screen; NSSize _maxSize; - CGFloat _maxTextWidth; + CGFloat _textWidthLimit; NSString *_preedit; NSRange _selRange; @@ -1393,14 +1405,14 @@ - (void)getCurrentScreen { } } -- (void)getMaxTextWidth { +- (void)getTextWidthLimit { SquirrelTheme *theme = _view.currentTheme; [self getCurrentScreen]; NSRect screenRect = [_screen visibleFrame]; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _maxTextWidth = floor((theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2); + _textWidthLimit = floor((theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2); if (theme.lineLength > 0) { - _maxTextWidth = MIN(floor(theme.lineLength), _maxTextWidth); + _textWidthLimit = MIN(floor(theme.lineLength), _textWidthLimit); } } @@ -1421,8 +1433,8 @@ - (void)show { NSEdgeInsets insets = _view.insets; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = [_screen visibleFrame]; - CGFloat maxTextHeight = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; - [textContainer setSize:NSMakeSize(_maxTextWidth + linePadding * 2, maxTextHeight)]; + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; + [textContainer setSize:NSMakeSize(_textWidthLimit + linePadding * 2, textHeightLimit)]; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); @@ -1430,7 +1442,7 @@ - (void)show { NSRect maxContentRect = NSInsetRect(contentRect, linePadding, 0); if (theme.lineLength > 0) { // fixed line length / text width if (_maxSize.width > 0) { // not applicable to status message where maxSize is set to 0 - maxContentRect.size.width = _maxTextWidth; + maxContentRect.size.width = _textWidthLimit; } } if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) @@ -1442,7 +1454,7 @@ - (void)show { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - [textContainer setSize:NSMakeSize(_maxSize.width + linePadding * 2, maxTextHeight)]; + [textContainer setSize:NSMakeSize(_maxSize.width + linePadding * 2, textHeightLimit)]; } } if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) @@ -1451,7 +1463,7 @@ - (void)show { _maxSize.height = NSHeight(maxContentRect); } else { maxContentRect.size.height = _maxSize.height; - [textContainer setSize:NSMakeSize(_maxTextWidth + linePadding * 2, _maxSize.height)]; + [textContainer setSize:NSMakeSize(_textWidthLimit + linePadding * 2, _maxSize.height)]; } } } @@ -1525,9 +1537,6 @@ - (void)show { NSRect textFrameRect = NSMakeRect(NSMinX(frameRect) + insets.left, NSMinY(frameRect) + insets.bottom, NSWidth(frameRect) - insets.left - insets.right, NSHeight(frameRect) - insets.top - insets.bottom); - if (@available(macOS 12.0, *)) { - textFrameRect = NSInsetRect(textFrameRect, linePadding, 0); - } [_view.textView setBoundsRotation:0.0]; [_view setBoundsOrigin:NSZeroPoint]; [_view.textView setBoundsOrigin:NSZeroPoint]; @@ -1600,14 +1609,14 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng return YES; }]; NSRect container = [_view.layoutManager usageBoundsForTextContainer]; - *maxLineLength = MAX(MIN(_maxTextWidth, ceil(NSWidth(container))), _maxSize.width); + *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } else { NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; CGFloat rangeEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); NSRect container = NSInsetRect([_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer], _view.textView.textContainer.lineFragmentPadding, 0); - *maxLineLength = MAX(MIN(_maxTextWidth, ceil(NSWidth(container))), _maxSize.width); + *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } } @@ -1664,16 +1673,16 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; - [self getMaxTextWidth]; + [self getTextWidthLimit]; if (theme.lineLength > 0) { - _maxSize.width = MIN(floor(theme.lineLength), _maxTextWidth); + _maxSize.width = MIN(floor(theme.lineLength), _textWidthLimit); } NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width); [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; - NSTextStorage *text = _view.textView.textStorage; + NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); @@ -1723,7 +1732,6 @@ - (void)showPreedit:(NSString *)preedit [sep addAttribute:NSVerticalGlyphFormAttributeName value:@NO range:NSMakeRange(0, sep.length)]; - [sep fixAttributesInRange:NSMakeRange(0, sep.length)]; CGFloat separatorWidth = theme.linear ? NSWidth([sep boundingRectWithSize:NSZeroSize options:0]) : 0.0; _view.separatorWidth = separatorWidth; @@ -1908,7 +1916,6 @@ - (void)showPreedit:(NSString *)preedit paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength/2], [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; } - NSFont *pagingFont = theme.pagingAttrs[NSFontAttributeName]; [paging addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(0, paging.length)]; @@ -1950,7 +1957,7 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message } - (void)showStatus:(NSString *)message { - [self getMaxTextWidth]; + [self getTextWidthLimit]; SquirrelTheme *theme = _view.currentTheme; _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width, @@ -1958,7 +1965,7 @@ - (void)showStatus:(NSString *)message { [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; - NSTextStorage *text = _view.textView.textStorage; + NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; [text appendAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]]; [text addAttribute:NSParagraphStyleAttributeName @@ -2046,13 +2053,16 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { font = font.verticalFont; } CGFloat lineHeight = ceil(font.ascender - font.descender); + if (@available(macOS 12.0, *)) { + return lineHeight; + } NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; for (NSFontDescriptor *fallback in fallbackList) { NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; if (vertical) { fallbackFont = fallbackFont.verticalFont; } - lineHeight = MAX(lineHeight, ceil(MIN(fallbackFont.ascender - fallbackFont.descender, NSHeight([fallbackFont boundingRectForFont])))); + lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); } return lineHeight; } From 92460fd2ba627e0e124e0dad20f74a890ca5b131 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 5 Aug 2023 11:42:58 +0200 Subject: [PATCH 124/164] prevent commit keystroke being hijacked --- SquirrelInputController.m | 1 + 1 file changed, 1 insertion(+) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index bb9660be0..616b2afe6 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -539,6 +539,7 @@ - (void)rimeConsumeCommittedText RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; [self commitString:commitText]; rime_get_api()->free_commit(&commit); } From 123ad665717e97ccd2117cc151dab1d8ccf19b8f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 6 Aug 2023 23:37:39 +0200 Subject: [PATCH 125/164] silence warnings --- README.md | 2 +- SquirrelApplicationDelegate.m | 12 ++---------- SquirrelInputController.m | 4 ++-- SquirrelPanel.m | 19 ++++++++++++------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ccdc17c09..d22a83346 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ 安裝輸入法 --- -本品適用於 macOS 10.9+ +本品適用於 macOS 10.6+ 初次安裝,如果在部份應用程序中打不出字,請註銷並重新登錄。 diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 82350ad3e..79fd5effc 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -184,22 +184,14 @@ - (BOOL)problematicLaunchDetected error:nil]; if (archive) { NSDate *previousLaunch; - if (@available(macOS 10.13, *)) { - previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; - } else { - previousLaunch = [NSKeyedUnarchiver unarchiveObjectWithData:archive]; - } + previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate *now = [NSDate date]; NSData *record; - if (@available(macOS 10.13, *)) { - record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; - } else { - record = [NSKeyedArchiver archivedDataWithRootObject:now]; - } + record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; [record writeToFile:logfile atomically:NO]; return detected; } diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 616b2afe6..8257ee002 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -539,7 +539,7 @@ - (void)rimeConsumeCommittedText RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; [self commitString:commitText]; rime_get_api()->free_commit(&commit); } @@ -623,7 +623,7 @@ - (void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } // update candidates diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 66076f16b..ae26a371e 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -32,6 +32,11 @@ - (CGPathRef)quartzPath { points[2].x, points[2].y); didClosePath = NO; break; + case NSBezierPathElementQuadraticCurveTo: + CGPathAddQuadCurveToPoint(path, NULL, points[0].x, points[0].y, + points[1].x, points[1].y); + didClosePath = NO; + break; case NSClosePathBezierPathElement: CGPathCloseSubpath(path); didClosePath = YES; @@ -1017,7 +1022,7 @@ - (void)updateLayer { if (theme.preeditBackgroundColor && (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { panelLayer.fillColor = [theme.preeditBackgroundColor CGColor]; - if (![candidateBlockPath isEmpty]) { + if (!candidateBlockPath.empty) { CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; candidateLayer.path = [candidateBlockPath quartzPath]; candidateLayer.fillColor = [theme.backgroundColor CGColor]; @@ -1062,7 +1067,7 @@ - (void)updateLayer { [self.layer addSublayer:pagingLayer]; } if (theme.highlightedPreeditColor) { - if (![highlightedPreeditPath isEmpty]) { + if (!highlightedPreeditPath.empty) { CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; @@ -1070,7 +1075,7 @@ - (void)updateLayer { [self.layer addSublayer:highlightedPreeditLayer]; } } - if (theme.borderColor && ![borderPath isEmpty]) { + if (theme.borderColor && !borderPath.empty) { CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; borderLayer.path = [borderPath quartzPath]; borderLayer.fillColor = [theme.borderColor CGColor]; @@ -1408,7 +1413,7 @@ - (void)getCurrentScreen { - (void)getTextWidthLimit { SquirrelTheme *theme = _view.currentTheme; [self getCurrentScreen]; - NSRect screenRect = [_screen visibleFrame]; + NSRect screenRect = _screen.visibleFrame; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); _textWidthLimit = floor((theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2); if (theme.lineLength > 0) { @@ -1432,7 +1437,7 @@ - (void)show { CGFloat linePadding = textContainer.lineFragmentPadding; NSEdgeInsets insets = _view.insets; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - NSRect screenRect = [_screen visibleFrame]; + NSRect screenRect = _screen.visibleFrame; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; [textContainer setSize:NSMakeSize(_textWidthLimit + linePadding * 2, textHeightLimit)]; @@ -1728,7 +1733,7 @@ - (void)showPreedit:(NSString *)preedit } // separator - NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; + NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; [sep addAttribute:NSVerticalGlyphFormAttributeName value:@NO range:NSMakeRange(0, sep.length)]; @@ -1886,7 +1891,7 @@ - (void)showPreedit:(NSString *)preedit [paging appendAttributedString:pageDownString]; [text appendAttributedString:[[NSAttributedString alloc] - initWithString:theme.linear ? @" " : @"\n" + initWithString:theme.linear ? @" " : @"\n" attributes:theme.attrs]]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; From 692239871f4b5198de36d9614c2d89a92d303b64 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 10 Aug 2023 11:27:32 +0200 Subject: [PATCH 126/164] dodge cursor effects view in macOS 14 --- SquirrelInputController.m | 22 +++++++++++----------- SquirrelPanel.m | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 8257ee002..bacfe3399 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -22,8 +22,6 @@ @implementation SquirrelInputController { NSString *_preeditString; NSAttributedString *_originalString; NSString *_composedString; - NSRange _selRange; - NSUInteger _caretPos; NSArray *_candidates; NSUInteger _lastModifier; int _lastEventCount; @@ -426,16 +424,7 @@ - (void)showPreeditString:(NSString *)preedit caretPos:(NSUInteger)pos { //NSLog(@"showPreeditString: '%@'", preedit); - - if ([_preeditString isEqualToString:preedit] && - NSEqualRanges(_selRange, range) && _caretPos == pos) { - return; - } - _preeditString = preedit; - _selRange = range; - _caretPos = pos; - //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", // range.location, range.length, pos); NSDictionary *attrs; @@ -474,6 +463,17 @@ - (void)showPanelWithPreedit:(NSString *)preedit NSRect inputPos; [_currentClient attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; + if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view + if (_lastModifier & OSX_CAPITAL_MASK) { + if (NSHeight(inputPos) > NSWidth(inputPos)) { + inputPos.size.height += 26; + inputPos.origin.y -= 26; + } else { + inputPos.size.width += 33; + inputPos.origin.x -= 33; + } + } + } panel.position = inputPos; panel.inputController = self; [panel showPreedit:preedit diff --git a/SquirrelPanel.m b/SquirrelPanel.m index ae26a371e..a9b4bff74 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1273,7 +1273,7 @@ - (instancetype)init { self.alphaValue = 1.0; // _window.level = NSScreenSaverWindowLevel + 1; // ^ May fix visibility issue in fullscreen games. - self.level = CGShieldingWindowLevel(); + self.level = kCGCursorWindowLevel - 10; self.hasShadow = NO; self.opaque = NO; self.displaysWhenScreenProfileChanges = YES; From fff377191eaaec907bf7ec056932b2d117bbaf5b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 10 Aug 2023 11:28:12 +0200 Subject: [PATCH 127/164] catch errors --- input_source.m | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/input_source.m b/input_source.m index 269ba3933..0acd1db33 100644 --- a/input_source.m +++ b/input_source.m @@ -38,13 +38,21 @@ void ActivateInputSource(int enabled_modes) { ((enabled_modes & HANT_INPUT_MODE) != 0)) || ([sourceID isEqualToString:kCantInputModeID] && ((enabled_modes & CANT_INPUT_MODE) != 0))) { - TISEnableInputSource(inputSource); - NSLog(@"Enabled input source: %@", sourceID); - CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceIsSelectCapable); - if (CFBooleanGetValue(isSelectable)) { - TISSelectInputSource(inputSource); - NSLog(@"Selected input source: %@", sourceID); + OSStatus enableError = TISEnableInputSource(inputSource); + if (enableError) { + NSLog(@"Error %d. Failed to enable input mode: %@", enableError, sourceID); + } else { + NSLog(@"Enabled input mode: %@", sourceID); + CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( + inputSource, kTISPropertyInputSourceIsSelectCapable); + if (CFBooleanGetValue(isSelectable)) { + OSStatus selectError = TISSelectInputSource(inputSource); + if (selectError) { + NSLog(@"Error %d. Failed to select input mode: %@", selectError, sourceID); + } else { + NSLog(@"Selected input mode: %@", sourceID); + } + } } } } @@ -65,8 +73,12 @@ void DeactivateInputSource(void) { CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( inputSource, kTISPropertyInputSourceIsEnabled)); if (CFBooleanGetValue(isEnabled)) { - TISDisableInputSource(inputSource); - NSLog(@"Disabled input source: %@", sourceID); + OSStatus disableError = TISDisableInputSource(inputSource); + if (disableError) { + NSLog(@"Error %d. Failed to disable input source: %@", disableError, sourceID); + } else { + NSLog(@"Disabled input source: %@", sourceID); + } } } } @@ -100,10 +112,12 @@ int GetEnabledInputModes(void) { } CFRelease(sourceList); if (input_modes != 0) { - NSLog(@"EnabledInputModes: %d", input_modes); + NSLog(@"Enabled Input Modes: %d", input_modes); } else { - NSArray *languages = [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK", @"yue"]]; - for (NSString *lang in languages) { + NSArray *languages = + [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK"]]; + if (languages.count > 0) { + NSString *lang = languages.firstObject; if ([lang isEqualToString:@"zh-Hans"]) { input_modes |= HANS_INPUT_MODE; } else if ([lang isEqualToString:@"zh-Hant"]) { @@ -113,10 +127,10 @@ int GetEnabledInputModes(void) { } } if (input_modes != 0) { - NSLog(@"PreferredInputModes: %d", input_modes); + NSLog(@"Preferred Input Mode: %d", input_modes); } else { input_modes = HANS_INPUT_MODE; - NSLog(@"DefaultInputMode: %d", input_modes); + NSLog(@"Default Input Mode: %d", input_modes); } } return input_modes; From ac67fe0fc4ea9691efb9ec7ad4efd3494d4acb0d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 10 Aug 2023 11:28:26 +0200 Subject: [PATCH 128/164] icon --- Assets.xcassets/Contents.json | 3 + .../RimeTray.symbolset/Contents.json | 22 --- .../RimeTray.symbolset/rimeTray.fill.svg | 130 ----------------- .../RimeTray.symbolset/rimeTray.svg | 136 ------------------ Info.plist | 4 + Squirrel.xcodeproj/project.pbxproj | 24 +++- SquirrelPanel.m | 2 +- librime | 2 +- rime.pdf | Bin 49049 -> 47964 bytes rime.svg | 1 - rime_raw.pdf | Bin 50581 -> 0 bytes 11 files changed, 26 insertions(+), 298 deletions(-) delete mode 100644 Assets.xcassets/RimeTray.symbolset/Contents.json delete mode 100644 Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg delete mode 100644 Assets.xcassets/RimeTray.symbolset/rimeTray.svg delete mode 100644 rime.svg delete mode 100644 rime_raw.pdf diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json index 73c00596a..7f73912a4 100644 --- a/Assets.xcassets/Contents.json +++ b/Assets.xcassets/Contents.json @@ -2,5 +2,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "compression-type" : "automatic" } } diff --git a/Assets.xcassets/RimeTray.symbolset/Contents.json b/Assets.xcassets/RimeTray.symbolset/Contents.json deleted file mode 100644 index 6bb99f0f4..000000000 --- a/Assets.xcassets/RimeTray.symbolset/Contents.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "symbols" : [ - { - "filename" : "rimeTray.svg", - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "rimeTray.fill.svg", - "idiom" : "universal" - } - ] -} diff --git a/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg b/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg deleted file mode 100644 index 2dff326b2..000000000 --- a/Assets.xcassets/RimeTray.symbolset/rimeTray.fill.svg +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - Weight/Scale Variations - Ultralight - Thin - Light - Regular - Medium - Semibold - Bold - Heavy - Black - - - - - - - - - - - Design Variations - Symbols are supported in up to nine weights and three scales. - For optimal layout with text and other symbols, vertically align - symbols with the adjacent text. - - - - - - Margins - Leading and trailing margins on the left and right side of each symbol - can be adjusted by modifying the x-location of the margin guidelines. - Modifications are automatically applied proportionally to all - scales and weights. - - - - Exporting - Symbols should be outlined when exporting to ensure the - design is preserved when submitting to Xcode. - Template v.4.0 - Requires Xcode 14 or greater - Generated from custom.123.rectangle.fill - Typeset at 100 points - Small - Medium - Large - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Assets.xcassets/RimeTray.symbolset/rimeTray.svg b/Assets.xcassets/RimeTray.symbolset/rimeTray.svg deleted file mode 100644 index 30df94e44..000000000 --- a/Assets.xcassets/RimeTray.symbolset/rimeTray.svg +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - Weight/Scale Variations - Ultralight - Thin - Light - Regular - Medium - Semibold - Bold - Heavy - Black - - - - - - - - - - - Design Variations - Symbols are supported in up to nine weights and three scales. - For optimal layout with text and other symbols, vertically align - symbols with the adjacent text. - - - - - - Margins - Leading and trailing margins on the left and right side of each symbol - can be adjusted by modifying the x-location of the margin guidelines. - Modifications are automatically applied proportionally to all - scales and weights. - - - - Exporting - Symbols should be outlined when exporting to ensure the - design is preserved when submitting to Xcode. - Template v.4.0 - Requires Xcode 14 or greater - Generated from custom.123.rectangle - Typeset at 100 points - Small - Medium - Large - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Info.plist b/Info.plist index f8a9567f1..0d79502dd 100644 --- a/Info.plist +++ b/Info.plist @@ -145,5 +145,9 @@ tsInputMethodIconFileKey rime.pdf + tsInputMethodAlternateIconFileKey + rime.pdf + tsInputMethodPaletteIconFileKey + rime.pdf diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index bce2c2627..c2e525315 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -32,7 +32,6 @@ 44AEBC7521F569FD00344375 /* key_bindings.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7221F569CF00344375 /* key_bindings.yaml */; }; 44AEBC7621F569FD00344375 /* punctuation.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 44AEBC7121F569CF00344375 /* punctuation.yaml */; }; 44CD640C15E2646B0021234E /* librime.1.dylib in Copy 3rd-party Frameworks */ = {isa = PBXBuildFile; fileRef = 44CD640915E2633D0021234E /* librime.1.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 44CD7D9E1828D981006E9222 /* rime.pdf */; }; 44E21A9016A653E700C2B08F /* rime_deployer in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8E16A653E700C2B08F /* rime_deployer */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44E21A9116A653E700C2B08F /* rime_dict_manager in CopyFiles */ = {isa = PBXBuildFile; fileRef = 44E21A8F16A653E700C2B08F /* rime_dict_manager */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 44F7708E152B3334005CF491 /* dsa_pub.pem */; }; @@ -84,6 +83,7 @@ A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; D26434552706A15100857391 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D26434542706A15100857391 /* QuartzCore.framework */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; + F4DEA7A62A8C1B8A002D4DAB /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -221,7 +221,6 @@ 44AEBC7221F569CF00344375 /* key_bindings.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = key_bindings.yaml; path = data/plum/key_bindings.yaml; sourceTree = ""; }; 44CB5E872585EFAE0022654F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 44CD640915E2633D0021234E /* librime.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librime.1.dylib; path = lib/librime.1.dylib; sourceTree = ""; }; - 44CD7D9E1828D981006E9222 /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; 44DA191A152B8CB600FB8EF0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; @@ -281,6 +280,7 @@ F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; + F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -306,8 +306,8 @@ 44AC95171430CF6000C888FB /* SquirrelApplicationDelegate.m */, 44AC95181430CF6000C888FB /* SquirrelInputController.h */, 44AC95191430CF6000C888FB /* SquirrelInputController.m */, - A47C48DE105E8CE8006D528B /* macos_keycode.m */, A44571AB0DBF42C200F793F9 /* macos_keycode.h */, + A47C48DE105E8CE8006D528B /* macos_keycode.m */, 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, 4443A8391828CC5100731305 /* input_source.m */, 29B97316FDCFA39411CA2CEA /* main.m */, @@ -366,9 +366,9 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, - 44CD7D9E1828D981006E9222 /* rime.pdf */, 44F7708E152B3334005CF491 /* dsa_pub.pem */, A4FC48C90F6530EF0069BE81 /* Localizable.strings */, 8D1107310486CEB800E47090 /* Info.plist */, @@ -543,9 +543,9 @@ 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, + F4DEA7A62A8C1B8A002D4DAB /* rime.pdf in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, - 44CD7D9F1828D981006E9222 /* rime.pdf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -608,13 +608,17 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; - CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 0.17.2; DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", @@ -650,6 +654,7 @@ OTHER_LDFLAGS = "-lrime.1"; PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel; PRODUCT_NAME = Squirrel; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = app; }; @@ -658,12 +663,16 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; - CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 0.17.2; DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", @@ -698,6 +707,7 @@ OTHER_LDFLAGS = "-lrime.1"; PRODUCT_BUNDLE_IDENTIFIER = im.rime.inputmethod.Squirrel; PRODUCT_NAME = Squirrel; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; WRAPPER_EXTENSION = app; }; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a9b4bff74..a4f76e0f8 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1283,7 +1283,7 @@ - (instancetype)init { if (@available(macOS 10.14, *)) { _back = [[NSVisualEffectView alloc] init]; _back.blendingMode = NSVisualEffectBlendingModeBehindWindow; - _back.material = NSVisualEffectMaterialHUDWindow; + _back.material = NSVisualEffectMaterialUnderWindowBackground; _back.state = NSVisualEffectStateActive; _back.wantsLayer = YES; _back.layer.mask = _view.shape; diff --git a/librime b/librime index 0360726d0..8269def7d 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 0360726d016fb0c1696a2e25b2a44730dc7ae171 +Subproject commit 8269def7daefcc24fb60b001377b0e58b197f4cf diff --git a/rime.pdf b/rime.pdf index 37cb4fed29d2bf912ba10b1a2607e99de3e0b1f9..89a2f8ab114e0f5bdde16494f8949846a4c52cd5 100644 GIT binary patch delta 39246 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 literal 49049 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 diff --git a/rime.svg b/rime.svg deleted file mode 100644 index 4de46b9dc..000000000 --- a/rime.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/rime_raw.pdf b/rime_raw.pdf deleted file mode 100644 index 423b63726fec2106e74d40645d2c16bcadde862a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50581 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 Date: Sat, 12 Aug 2023 01:16:21 +0200 Subject: [PATCH 129/164] boost 1.82 --- INSTALL.md | 2 +- autobuild.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index c31f03eec..9ecc67a80 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_82_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/autobuild.sh b/autobuild.sh index 5804b05d4..804933579 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -8,7 +8,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_78_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_82_0" export BUILD_UNIVERSAL=1 From fce4438b6e13832a58dac56d2fc9da7a83429f52 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 13 Aug 2023 11:21:00 +0200 Subject: [PATCH 130/164] simplify codes --- SquirrelInputController.m | 22 +-- SquirrelPanel.m | 285 +++++++++++++++++++------------------- 2 files changed, 153 insertions(+), 154 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index bacfe3399..518bde054 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -629,8 +629,7 @@ - (void)rimeUpdate // update candidates NSMutableArray *candidates = [NSMutableArray array]; NSMutableArray *comments = [NSMutableArray array]; - NSUInteger i; - for (i = 0; i < ctx.menu.num_candidates; ++i) { + for (NSUInteger i = 0; i < ctx.menu.num_candidates; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; if (ctx.menu.candidates[i].comment) { [comments addObject:@(ctx.menu.candidates[i].comment)]; @@ -638,18 +637,21 @@ - (void)rimeUpdate [comments addObject:@""]; } } - NSArray *labels; + NSMutableArray *labels = [NSMutableArray array]; if (ctx.menu.select_keys) { - labels = @[@(ctx.menu.select_keys)]; + NSString *selectKeys = [@(ctx.menu.select_keys) stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + [labels addObject:[selectKeys substringWithRange:NSMakeRange(i, 1)]]; + } } else if (ctx.select_labels) { - NSMutableArray *selectLabels = [NSMutableArray array]; - for (i = 0; i < ctx.menu.page_size; ++i) { - char *label_str = ctx.select_labels[i]; - [selectLabels addObject:@(label_str)]; + for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + [labels addObject:@(ctx.select_labels[i])]; } - labels = selectLabels; } else { - labels = @[]; + NSString *labelString = @"1234567890"; + for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + [labels addObject:[labelString substringWithRange:NSMakeRange(i, 1)]]; + } } [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) selRange:NSMakeRange(start, end - start) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a4f76e0f8..6633a8308 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -102,6 +102,11 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *statusParagraphStyle; +@property(nonatomic, strong, readonly) NSAttributedString *symbolBackFill; +@property(nonatomic, strong, readonly) NSAttributedString *symbolBackStroke; +@property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; +@property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; + @property(nonatomic, strong, readonly) NSString *prefixLabelFormat; @property(nonatomic, strong, readonly) NSString *suffixLabelFormat; @property(nonatomic, strong, readonly) NSString *statusMessageType; @@ -249,6 +254,26 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _preeditHighlightedAttrs = preeditHighlightedAttrs; _pagingAttrs = pagingAttrs; _pagingHighlightedAttrs = pagingHighlightedAttrs; + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" + attributes:@{NSGlyphInfoAttributeName: + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" + forFont:_pagingAttrs[NSFontAttributeName] + baseString:@"◀"]}]; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" + attributes:@{NSGlyphInfoAttributeName: + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" + forFont:_pagingAttrs[NSFontAttributeName] + baseString:@"◁"]}]; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" + attributes:@{NSGlyphInfoAttributeName: + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" + forFont:_pagingAttrs[NSFontAttributeName] + baseString:@"▶"]}]; + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" + attributes:@{NSGlyphInfoAttributeName: + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" + forFont:_pagingAttrs[NSFontAttributeName] + baseString:@"▷"]}]; } - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -278,7 +303,6 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSMutableArray *pagingPaths; @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; -@property(nonatomic, readonly) NSTextLayoutManager *layoutManager API_AVAILABLE(macos(12.0)); @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, assign) CGFloat separatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; @@ -323,6 +347,10 @@ - (BOOL)isDark { return NO; } +- (BOOL)allowsVibrancy { + return YES; +} + - (SquirrelTheme *)selectTheme:(BOOL)isDark { return isDark ? _darkTheme : _defaultTheme; } @@ -341,15 +369,15 @@ - (instancetype)initWithFrame:(NSRect)frameRect { } if (@available(macOS 12.0, *)) { - _layoutManager = [[NSTextLayoutManager alloc] init]; - _layoutManager.usesFontLeading = NO; + NSTextLayoutManager *textLayoutManager = [[NSTextLayoutManager alloc] init]; + textLayoutManager.usesFontLeading = NO; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; - _layoutManager.textContainer = textContainer; + textLayoutManager.textContainer = textContainer; NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; - [contentStorage addTextLayoutManager:_layoutManager]; + [contentStorage addTextLayoutManager:textLayoutManager]; _textView = [[NSTextView alloc] initWithFrame:frameRect - textContainer:_layoutManager.textContainer]; + textContainer:textLayoutManager.textContainer]; _textStorage = _textView.textContentStorage.textStorage; } else { NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; @@ -373,7 +401,6 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _shape = [[CAShapeLayer alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; - _textView.usesAdaptiveColorMappingForDarkAppearance = YES; } return self; } @@ -398,8 +425,10 @@ - (NSRect)setLineRectForRange:(NSRange)charRange BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; CGFloat refFontHeight = refFont.ascender - refFont.descender; CGFloat refBaseline = refFont.ascender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + lineHeight += style.lineSpacing; NSRect blockRect = NSZeroRect; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; @@ -415,11 +444,6 @@ - (NSRect)setLineRectForRange:(NSRange)charRange rect.size.height = lineHeight; usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); CGFloat alignment = verticalLayout ? lineHeight / 2 : refBaseline + MAX(0.0, lineHeight - refFontHeight) / 2; - if (style.lineSpacing > 0) { - rect.size.height = lineHeight + style.lineSpacing; - usedRect.size.height = MAX(NSHeight(usedRect), lineHeight + style.lineSpacing); - alignment += style.lineSpacing / 2; - } if (style.paragraphSpacing > 0 && NSMaxRange(lineCharRange) != _textStorage.length && [_textStorage.string characterAtIndex:NSMaxRange(lineCharRange) - 1] == '\n') { rect.size.height += style.paragraphSpacing; @@ -475,8 +499,8 @@ - (NSRect)setLineRectForRange:(NSRange)charRange // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { if (@available(macOS 12.0, *)) { - [_layoutManager ensureLayoutForRange:_layoutManager.textContentManager.documentRange]; - return NSInsetRect([_layoutManager usageBoundsForTextContainer], + [_textView.textLayoutManager ensureLayoutForRange:_textView.textContentStorage.documentRange]; + return NSInsetRect([_textView.textLayoutManager usageBoundsForTextContainer], -_textView.textContainer.lineFragmentPadding, 0); } else { [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; @@ -489,14 +513,16 @@ - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:range]; __block NSRect contentRect = NSZeroRect; - [_layoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeHighlight - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: + [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { contentRect = NSUnionRect(contentRect, segmentRect); return YES; }]; + CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:NSMaxRange(range)-1 effectiveRange:NULL] lineSpacing]; + contentRect.size.height += lineSpacing; return contentRect; } else { NSTextContainer *textContainer = _textView.textContainer; @@ -508,7 +534,9 @@ - (NSRect)contentRectForRange:(NSRange)range { effectiveRange:&firstLineRange]; if (NSMaxRange(glyphRange) <= NSMaxRange(firstLineRange)) { CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(firstLineRect) - textContainer.lineFragmentPadding; + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(firstLineRect) - textContainer.lineFragmentPadding; return NSMakeRect(NSMinX(firstLineRect) + startX, NSMinY(firstLineRect), endX - startX, NSHeight(firstLineRect)); } else { @@ -594,7 +622,7 @@ - (void) drawViewWithInsets:(NSEdgeInsets)insets } BOOL nearEmptyRect(NSRect rect) { - return rect.size.height * rect.size.width < 1; + return NSHeight(rect) * NSWidth(rect) < 1; } // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle @@ -602,14 +630,16 @@ BOOL nearEmptyRect(NSRect rect) { - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)leadingRect bodyRect:(NSRectPointer)bodyRect trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:charRange]; + CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:charRange.location effectiveRange:NULL] lineSpacing]; NSMutableArray *lineRects = [NSMutableArray arrayWithCapacity:2]; NSMutableArray *lineRanges = [NSMutableArray arrayWithCapacity:2]; - [_layoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded - usingBlock: + [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded + usingBlock: ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { if (!nearEmptyRect(segmentRect)) { + segmentRect.size.height += lineSpacing; [lineRects addObject:[NSValue valueWithRect:segmentRect]]; [lineRanges addObject:segmentRange]; } @@ -655,7 +685,9 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead effectiveRange:&leadingLineRange]; CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect) - textContainer.lineFragmentPadding; + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(leadingLineRect) - textContainer.lineFragmentPadding; *bodyRect = NSMakeRect(startX, NSMinY(leadingLineRect), endX - startX, NSHeight(leadingLineRect)); } else { @@ -663,7 +695,9 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 effectiveRange:&trailingLineRange]; - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(trailingLineRect) - textContainer.lineFragmentPadding; + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(trailingLineRect) - textContainer.lineFragmentPadding; CGFloat leftEdge = textContainer.lineFragmentPadding; if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { if (glyphRange.location == leadingLineRange.location) { @@ -802,7 +836,9 @@ - (void)updateLayer { } } else { NSPoint lineOrigin = NSZeroPoint; - CGFloat refFontSize = _preeditRange.length == 0 && _candidateRanges.count == 0 ? [theme.commentAttrs[NSFontAttributeName] pointSize] : [theme.attrs[NSFontAttributeName] pointSize]; + CGFloat refFontSize = _preeditRange.length == 0 && _candidateRanges.count == 0 + ? [theme.commentAttrs[NSFontAttributeName] pointSize] + : [theme.attrs[NSFontAttributeName] pointSize]; NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, refFontSize, (CFStringRef)@"zh")); if (theme.vertical) { refFont = refFont.verticalFont; @@ -855,11 +891,11 @@ - (void)updateLayer { NSRect trailingRect = NSZeroRect; [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); NSMutableArray *highlightedPreeditPoints; NSMutableArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated @@ -896,9 +932,12 @@ - (void)updateLayer { NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); if (preeditRange.length == 0) { leadingRect.origin.y += theme.linespace / 2; bodyRect.origin.y += theme.linespace / 2; @@ -1014,11 +1053,6 @@ - (void)updateLayer { panelLayer.path = [textContainerPath quartzPath]; panelLayer.fillColor = [theme.backgroundColor CGColor]; [self.layer addSublayer:panelLayer]; - if (@available(macOS 10.14, *)) { - if (theme.translucency > 0) { - panelLayer.opacity = 1.0 - theme.translucency; - } - } if (theme.preeditBackgroundColor && (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { panelLayer.fillColor = [theme.preeditBackgroundColor CGColor]; @@ -1031,6 +1065,11 @@ - (void)updateLayer { } CIFilter *backColorFilter = [CIFilter filterWithName:@"CISourceATopCompositing"]; panelLayer.compositingFilter = backColorFilter; + if (@available(macOS 10.14, *)) { + if (theme.translucency > 0) { + panelLayer.opacity = 1.0 - theme.translucency; + } + } if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; highlightedPath = _candidatePaths[_highlightedIndex]; @@ -1226,7 +1265,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingAttrs[NSFontAttributeName] = symbolFont; NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] + : [NSColor selectedMenuItemTextColor]; pagingHighlightedAttrs[NSFontAttributeName] = symbolFont; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; @@ -1365,7 +1405,8 @@ - (void)sendEvent:(NSEvent *)event { [self performWindowDragWithEvent:event]; } break; case NSEventTypeScrollWheel: { - CGFloat scrollThreshold = [_view.currentTheme.attrs[NSParagraphStyleAttributeName] minimumLineHeight]; + SquirrelTheme *theme = _view.currentTheme; + CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; if (event.phase == NSEventPhaseBegan) { _scrollLocus = NSZeroPoint; } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && @@ -1378,13 +1419,13 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { [_inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:(_view.currentTheme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { [_inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; @@ -1426,7 +1467,7 @@ - (void)show { SquirrelTheme *theme = _view.currentTheme; if (@available(macOS 10.14, *)) { - NSAppearance *requestedAppearance = theme.native ? nil : [NSAppearance appearanceNamed:NSAppearanceNameAqua]; + NSAppearance *requestedAppearance = [NSAppearance appearanceNamed:(_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; } @@ -1576,10 +1617,10 @@ - (BOOL)shouldBreakLineWithRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; NSUInteger __block lineCount = 0; - [_view.layoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: + [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { ++lineCount; return YES; @@ -1605,15 +1646,15 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; CGFloat __block rangeEdge; - [_view.layoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: + [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock: ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { rangeEdge = NSMaxX(segmentFrame); return YES; }]; - NSRect container = [_view.layoutManager usageBoundsForTextContainer]; + NSRect container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } else { @@ -1683,9 +1724,9 @@ - (void)showPreedit:(NSString *)preedit _maxSize.width = MIN(floor(theme.lineLength), _textWidthLimit); } NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, - theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width); + theme.edgeInset.height + theme.linespace / 2 + theme.paragraphStyle.lineSpacing, theme.edgeInset.width); - [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; @@ -1727,7 +1768,7 @@ - (void)showPreedit:(NSString *)preedit if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { - insets.bottom = theme.edgeInset.height; + insets.bottom = theme.edgeInset.height + theme.preeditParagraphStyle.lineSpacing; } insets.top = theme.edgeInset.height; } @@ -1742,36 +1783,19 @@ - (void)showPreedit:(NSString *)preedit // candidate items NSUInteger candidateBlockStart = text.length; - NSMutableArray *candidateRanges = [[NSMutableArray alloc] init]; + NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; NSUInteger lineStart = text.length; NSRange separatorRange = NSMakeRange(NSNotFound, 0); for (NSUInteger i = 0; i < candidates.count; ++i) { NSMutableAttributedString *item = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; NSDictionary *labelAttrs = (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { - NSString *prefixLabelString; NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - if (labels.count > 1 && i < labels.count) { - prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; - } else if (labels.count == 1 && i < [labels[0] length]) { - // custom: A. B. C... - NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; - prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; - } else { - // default: 1. 2. 3... - NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; - prefixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; - } - + NSString *prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] initWithString:prefixLabelString attributes:labelAttrs]]; @@ -1791,33 +1815,15 @@ - (void)showPreedit:(NSString *)preedit range:NSMakeRange(candidateStart, item.length - candidateStart)]; if (i < comments.count && [comments[i] length] != 0) { - [item appendAttributedString:[[NSAttributedString alloc] - initWithString:@" " - attributes:commentAttrs]]; - NSString *comment = comments[i]; + NSString *comment = [@" " stringByAppendingString:comments[i]]; [item appendAttributedString:[[NSAttributedString alloc] initWithString:comment attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { - NSString *suffixLabelString; NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - if (labels.count > 1 && i < labels.count) { - suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; - } else if (labels.count == 1 && i < [labels[0] length]) { - // custom: A. B. C... - NSString *labelCharacter = [[labels[0] substringWithRange:NSMakeRange(i, 1)] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; - suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; - } else { - // default: 1. 2. 3... - NSString *labelCharacter = [[NSString stringWithFormat:@"%lu", (i + 1) % 10] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; - suffixLabelString = [NSString stringWithFormat:labelFormat, labelCharacter]; - } + NSString *suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] initWithString:suffixLabelString attributes:labelAttrs]]; @@ -1858,36 +1864,19 @@ - (void)showPreedit:(NSString *)preedit NSRange pagingRange = NSMakeRange(NSNotFound, 0); if (numCandidates > 0 && theme.showPaging) { NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; - NSGlyphInfo *backFill = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"◀"]; - NSGlyphInfo *backStroke = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"◁"]; - NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] - initWithString:(pageNum ? @"◀" : @"◁") - attributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; - [pageUpString addAttribute:NSGlyphInfoAttributeName - value:(pageNum ? backFill : backStroke) - range:NSMakeRange(0, pageUpString.length)]; + + NSMutableAttributedString *pageUpString = [(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke) mutableCopy]; + [pageUpString addAttributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs) + range:NSMakeRange(0, pageUpString.length)]; [paging appendAttributedString:pageUpString]; [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; - NSGlyphInfo *forwardStroke = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"▷"]; - NSGlyphInfo *forwardFill = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" - forFont:theme.pagingAttrs[NSFontAttributeName] - baseString:@"▶"]; - NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] - initWithString:(lastPage ? @"▷" : @"▶") - attributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs)]; - [pageDownString addAttribute:NSGlyphInfoAttributeName - value:(lastPage ? forwardStroke : forwardFill) - range:NSMakeRange(0, pageDownString.length)]; + NSMutableAttributedString *pageDownString = [(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill) mutableCopy]; + [pageDownString addAttributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs) + range:NSMakeRange(0, pageDownString.length)]; [paging appendAttributedString:pageDownString]; [text appendAttributedString:[[NSAttributedString alloc] @@ -1966,8 +1955,8 @@ - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width, - theme.edgeInset.height, theme.edgeInset.width); - [_view.textView setLayoutOrientation:theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal]; + theme.edgeInset.height + theme.statusParagraphStyle.lineSpacing, theme.edgeInset.width); + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; NSTextStorage *text = _view.textStorage; @@ -2053,21 +2042,20 @@ - (void)hideStatus:(NSTimer *)timer { return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } -static CGFloat getLineHeight(NSFont *font, BOOL vertical) { +static CGFloat getLineHeight(NSFont *font, BOOL vertical, BOOL includeFallback) { if (vertical) { font = font.verticalFont; } CGFloat lineHeight = ceil(font.ascender - font.descender); - if (@available(macOS 12.0, *)) { - return lineHeight; - } - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; - for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; - if (vertical) { - fallbackFont = fallbackFont.verticalFont; + if (includeFallback) { + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; + } + lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); } - lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); } return lineHeight; } @@ -2311,30 +2299,37 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:labelFontSize]; - CGFloat fontHeight = getLineHeight(font, vertical); - CGFloat labelFontHeight = getLineHeight(labelFont, vertical); - CGFloat commentFontHeight = getLineHeight(commentFont, vertical); + CGFloat fontHeight = getLineHeight(font, vertical, NO); + CGFloat fontHeightMax = getLineHeight(font, vertical, YES); + CGFloat labelFontHeight = getLineHeight(labelFont, vertical, NO); + CGFloat labelFontHeightMax = getLineHeight(labelFont, vertical, YES); + CGFloat commentFontHeight = getLineHeight(commentFont, vertical, NO); + CGFloat commentFontHeightMax = getLineHeight(commentFont, vertical, YES); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); + CGFloat lineHeightMax = MAX(fontHeightMax, MAX(labelFontHeightMax, commentFontHeightMax)); NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; - preeditParagraphStyle.minimumLineHeight = fontHeight; - preeditParagraphStyle.maximumLineHeight = fontHeight; + preeditParagraphStyle.minimumLineHeight = (fontHeight + fontHeightMax) / 2; + preeditParagraphStyle.maximumLineHeight = (fontHeight + fontHeightMax) / 2; + preeditParagraphStyle.lineSpacing = (fontHeightMax - fontHeight) / 2; preeditParagraphStyle.paragraphSpacing = spacing; NSMutableParagraphStyle *paragraphStyle = [theme.paragraphStyle mutableCopy]; - paragraphStyle.minimumLineHeight = lineHeight; - paragraphStyle.maximumLineHeight = lineHeight; + paragraphStyle.minimumLineHeight = (lineHeight + lineHeightMax) / 2; + paragraphStyle.maximumLineHeight = (lineHeight + lineHeightMax) / 2; + paragraphStyle.lineSpacing = (lineHeightMax - lineHeight) / 2; paragraphStyle.paragraphSpacing = lineSpacing / 2; paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; - pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); - pagingParagraphStyle.maximumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); + pagingParagraphStyle.minimumLineHeight = getLineHeight(pagingFont, NO, NO); + pagingParagraphStyle.maximumLineHeight = getLineHeight(pagingFont, NO, NO); pagingParagraphStyle.paragraphSpacingBefore = pagingFont.leading; NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; - statusParagraphStyle.minimumLineHeight = commentFontHeight; - statusParagraphStyle.maximumLineHeight = commentFontHeight; + statusParagraphStyle.minimumLineHeight = (commentFontHeight + commentFontHeightMax) / 2; + statusParagraphStyle.maximumLineHeight = (commentFontHeight + commentFontHeightMax) / 2; + statusParagraphStyle.lineSpacing = (commentFontHeightMax - commentFontHeight) / 2; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -2358,10 +2353,12 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingAttrs[NSFontAttributeName] = pagingFont; pagingHighlightedAttrs[NSFontAttributeName] = pagingFont; + CGFloat labelBaselineShift = vertical && labelFont.verticalFont.isVertical ? 0.0 + : MAX(font.ascender, MAX(labelFont.ascender, commentFont.ascender)) - lineHeight/2 - labelFont.ascender + labelFontHeight/2; attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelBaselineShift); + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelBaselineShift); commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); From 0a2f36a83948288fc813f616d2fc91e54634e2a7 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sun, 13 Aug 2023 14:44:57 +0200 Subject: [PATCH 131/164] min MacOS version --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 9ecc67a80..f15901bc5 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -115,10 +115,10 @@ To build only for the native architecture, pass variable `ARCHS` to `make`: ``` sh # for Mac computers with Apple Silicon -make ARCHS='arm64' MACOSX_DEPLOYMENT_TARGET='10.13' +make ARCHS='arm64' MACOSX_DEPLOYMENT_TARGET='10.14' # for Intel-based Mac -make ARCHS='x86_64' MACOSX_DEPLOYMENT_TARGET='10.13' +make ARCHS='x86_64' MACOSX_DEPLOYMENT_TARGET='10.14' ``` ## Install it on your Mac From f6698c9911391ae02101f8391f3e9c356b93004d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 14 Aug 2023 09:31:19 +0200 Subject: [PATCH 132/164] clean codes --- SquirrelInputController.m | 41 ++++++++++++++++++++++----------------- SquirrelPanel.m | 12 ++++++++---- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 518bde054..253a21299 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -10,7 +10,7 @@ @interface SquirrelInputController (Private) - (void)createSession; - (void)destroySession; -- (void)rimeConsumeCommittedText; +- (BOOL)rimeConsumeCommittedText; - (void)rimeUpdate; - (void)updateAppOptions; @end @@ -18,7 +18,6 @@ - (void)updateAppOptions; const int N_KEY_ROLL_OVER = 50; @implementation SquirrelInputController { - id _currentClient; NSString *_preeditString; NSAttributedString *_originalString; NSString *_composedString; @@ -50,7 +49,6 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - _currentClient = sender; NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @@ -62,7 +60,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender } } - NSString *app = [_currentClient bundleIdentifier]; + NSString *app = [sender bundleIdentifier]; if (![_currentApp isEqualToString:app]) { _currentApp = [app copy]; @@ -302,23 +300,24 @@ - (void)activateServer:(id)sender //NSLog(@"activateServer:"); NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { - keyboardLayout = NULL; + keyboardLayout = nil; } else if ([keyboardLayout isEqualToString:@"default"]) { keyboardLayout = @"com.apple.keylayout.ABC"; } else if (![keyboardLayout hasPrefix:@"com.apple.keylayout."]) { - keyboardLayout = [NSString stringWithFormat:@"com.apple.keylayout.%@", keyboardLayout]; + keyboardLayout = [@"com.apple.keylayout." stringByAppendingString:keyboardLayout]; } if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } _preeditString = @""; + _composedString = @""; + _originalString = [[NSAttributedString alloc] initWithString:@""]; } - (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient { //NSLog(@"initWithServer:delegate:client:"); if (self = [super initWithServer:server delegate:delegate client:inputClient]) { - _currentClient = inputClient; [self createSession]; } return self; @@ -327,7 +326,6 @@ - (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client: - (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); - [NSApp.squirrelAppDelegate.panel hide]; [self commitComposition:sender]; } @@ -409,8 +407,8 @@ - (void)dealloc - (void)commitString:(NSString *)string { //NSLog(@"commitString:"); - [_currentClient insertText:string - replacementRange:NSMakeRange(NSNotFound, 0)]; + [self.client insertText:string + replacementRange:self.client.markedRange]; _preeditString = @""; _composedString = @""; @@ -443,9 +441,9 @@ - (void)showPreeditString:(NSString *)preedit attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; [attrString setAttributes:attrs range:rawRange]; } - [_currentClient setMarkedText:attrString - selectionRange:NSMakeRange(pos, 0) - replacementRange:NSMakeRange(NSNotFound, 0)]; + [self.client setMarkedText:attrString + selectionRange:NSMakeRange(pos, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; } - (void)showPanelWithPreedit:(NSString *)preedit @@ -461,7 +459,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; NSRect inputPos; - [_currentClient attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; + [self.client attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view if (_lastModifier & OSX_CAPITAL_MASK) { @@ -497,7 +495,7 @@ @implementation SquirrelInputController (Private) - (void)createSession { - NSString *app = [_currentClient bundleIdentifier]; + NSString *app = [self.client bundleIdentifier]; NSLog(@"createSession: %@", app); _currentApp = [app copy]; _session = rime_get_api()->create_session(); @@ -534,21 +532,28 @@ - (void)destroySession [self clearChord]; } -- (void)rimeConsumeCommittedText +- (BOOL)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + if (_preeditString.length == 0) { + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + } [self commitString:commitText]; rime_get_api()->free_commit(&commit); + return YES; } + return NO; } - (void)rimeUpdate { //NSLog(@"rimeUpdate"); - [self rimeConsumeCommittedText]; + if ([self rimeConsumeCommittedText]) { + return; + } + BOOL switcher = rime_get_api()->get_option(_session, "dumb"); RIME_STRUCT(RimeStatus, status); diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 6633a8308..49c6e7075 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1323,7 +1323,7 @@ - (instancetype)init { if (@available(macOS 10.14, *)) { _back = [[NSVisualEffectView alloc] init]; _back.blendingMode = NSVisualEffectBlendingModeBehindWindow; - _back.material = NSVisualEffectMaterialUnderWindowBackground; + _back.material = NSVisualEffectMaterialHUDWindow; _back.state = NSVisualEffectStateActive; _back.wantsLayer = YES; _back.layer.mask = _view.shape; @@ -1460,6 +1460,8 @@ - (void)getTextWidthLimit { if (theme.lineLength > 0) { _textWidthLimit = MIN(floor(theme.lineLength), _textWidthLimit); } + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - theme.edgeInset.height * 2; + _view.textView.textContainer.size = NSMakeSize(_textWidthLimit + theme.hilitedCornerRadius * 2, textHeightLimit); } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect @@ -1480,7 +1482,6 @@ - (void)show { CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = _screen.visibleFrame; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; - [textContainer setSize:NSMakeSize(_textWidthLimit + linePadding * 2, textHeightLimit)]; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); @@ -1583,6 +1584,9 @@ - (void)show { NSRect textFrameRect = NSMakeRect(NSMinX(frameRect) + insets.left, NSMinY(frameRect) + insets.bottom, NSWidth(frameRect) - insets.left - insets.right, NSHeight(frameRect) - insets.top - insets.bottom); + if (@available(macOS 12.0, *)) { + textFrameRect = NSInsetRect(textFrameRect, linePadding, 0); + } [_view.textView setBoundsRotation:0.0]; [_view setBoundsOrigin:NSZeroPoint]; [_view.textView setBoundsOrigin:NSZeroPoint]; @@ -1719,6 +1723,8 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; + _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; [self getTextWidthLimit]; if (theme.lineLength > 0) { _maxSize.width = MIN(floor(theme.lineLength), _textWidthLimit); @@ -1726,8 +1732,6 @@ - (void)showPreedit:(NSString *)preedit NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, theme.edgeInset.height + theme.linespace / 2 + theme.paragraphStyle.lineSpacing, theme.edgeInset.width); - _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; - _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); From 204d42b11ee4914bd53d10b26536369e7e0b5e3d Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 14 Aug 2023 21:04:47 +0200 Subject: [PATCH 133/164] space --- SquirrelInputController.m | 4 ++-- SquirrelPanel.m | 16 ++++++---------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 253a21299..73a85bce3 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -538,7 +538,7 @@ - (BOOL)rimeConsumeCommittedText if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); if (_preeditString.length == 0) { - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; } [self commitString:commitText]; rime_get_api()->free_commit(&commit); @@ -628,7 +628,7 @@ - (void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } // update candidates diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 49c6e7075..c23abe578 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1778,12 +1778,8 @@ - (void)showPreedit:(NSString *)preedit } // separator - NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; - [sep addAttribute:NSVerticalGlyphFormAttributeName - value:@NO - range:NSMakeRange(0, sep.length)]; - CGFloat separatorWidth = theme.linear ? NSWidth([sep boundingRectWithSize:NSZeroSize options:0]) : 0.0; - _view.separatorWidth = separatorWidth; + NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; + _view.separatorWidth = theme.linear ? sep.size.width : 0.0; // candidate items NSUInteger candidateBlockStart = text.length; @@ -1804,7 +1800,7 @@ - (void)showPreedit:(NSString *)preedit initWithString:prefixLabelString attributes:labelAttrs]]; if (!theme.linear) { // get the label size for indent - labelWidth = NSWidth([item boundingRectWithSize:NSZeroSize options:0]); + labelWidth = item.size.width; } } @@ -1884,7 +1880,7 @@ - (void)showPreedit:(NSString *)preedit [paging appendAttributedString:pageDownString]; [text appendAttributedString:[[NSAttributedString alloc] - initWithString:theme.linear ? @" " : @"\n" + initWithString:theme.linear ? @" " : @"\n" attributes:theme.attrs]]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; @@ -2450,8 +2446,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:MIN(cornerRadius, lineHeight/2) - hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/3) + [theme setCornerRadius:MIN(cornerRadius, lineHeightMax/2) + hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeightMax/3) edgeInset:edgeInset linespace:lineSpacing preeditLinespace:spacing From df207fcb18b4aafbff524c19d1db56ad73a89981 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 16 Aug 2023 03:01:05 +0200 Subject: [PATCH 134/164] inline placeholder --- SquirrelInputController.m | 87 ++++++++++++++++++++++++++++----------- data/squirrel.yaml | 7 ++++ librime | 2 +- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 73a85bce3..f02fce8dc 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -18,9 +18,11 @@ - (void)updateAppOptions; const int N_KEY_ROLL_OVER = 50; @implementation SquirrelInputController { - NSString *_preeditString; + NSMutableAttributedString *_preeditString; NSAttributedString *_originalString; NSString *_composedString; + NSRange _selRange; + NSUInteger _caretPos; NSArray *_candidates; NSUInteger _lastModifier; int _lastEventCount; @@ -28,6 +30,9 @@ @implementation SquirrelInputController { NSString *_schemaId; BOOL _inlinePreedit; BOOL _inlineCandidate; + // app-specific bug fix + BOOL _inlinePlaceHolder; + BOOL _panellessCommitFix; // for chord-typing int _chordKeyCodes[N_KEY_ROLL_OVER]; int _chordModifiers[N_KEY_ROLL_OVER]; @@ -309,9 +314,9 @@ - (void)activateServer:(id)sender if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } - _preeditString = @""; - _composedString = @""; - _originalString = [[NSAttributedString alloc] initWithString:@""]; + _preeditString = nil; + _originalString = nil; + _composedString = nil; } - (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient @@ -343,9 +348,8 @@ - (void)deactivateServer:(id)sender - (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); - if (_session && _preeditString.length > 0) { - NSString *composition = [self composedString:sender]; - [self commitString:composition]; + if (_session) { + [self commitString:[self composedString:sender]]; rime_get_api()->clear_composition(_session); } } @@ -399,22 +403,49 @@ - (NSArray *)candidates:(id)sender return _candidates; } +- (void)hidePalettes +{ + [NSApp.squirrelAppDelegate.panel hide]; +} + - (void)dealloc { [self destroySession]; } -- (void)commitString:(NSString *)string +- (NSRange)selectionRange +{ + return NSMakeRange(_caretPos, 0); +} + +- (NSRange)replacementRange +{ + return NSMakeRange(NSNotFound, NSNotFound); +} + +- (void)commitString:(id)string { //NSLog(@"commitString:"); [self.client insertText:string - replacementRange:self.client.markedRange]; + replacementRange:self.replacementRange]; + [self hidePalettes]; - _preeditString = @""; - _composedString = @""; - _originalString = [[NSAttributedString alloc] initWithString:@""]; + _composedString = nil; + _originalString = nil; + _preeditString = nil; +} - [NSApp.squirrelAppDelegate.panel hide]; +- (void)cancelComposition +{ + [self commitString:[self originalString:self.client]]; + rime_get_api()->clear_composition(_session); +} + +- (void)updateComposition +{ + [self.client setMarkedText:_preeditString + selectionRange:self.selectionRange + replacementRange:self.replacementRange]; } - (void)showPreeditString:(NSString *)preedit @@ -422,28 +453,34 @@ - (void)showPreeditString:(NSString *)preedit caretPos:(NSUInteger)pos { //NSLog(@"showPreeditString: '%@'", preedit); - _preeditString = preedit; + if ([preedit isEqualToString:_preeditString.string] && + NSEqualRanges(range, _selRange) && pos == _caretPos) { + if (_inlinePlaceHolder) { + [self updateComposition]; + } + return; + } + _selRange = range; + _caretPos = pos; //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", // range.location, range.length, pos); NSDictionary *attrs; - NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:preedit]; + _preeditString = [[NSMutableAttributedString alloc] initWithString:preedit]; if (range.location > 0) { NSRange convertedRange = NSMakeRange(0, range.location); attrs = [self markForStyle:kTSMHiliteConvertedText atRange:convertedRange]; - [attrString setAttributes:attrs range:convertedRange]; + [_preeditString addAttributes:attrs range:convertedRange]; } if (range.location < pos) { attrs = [self markForStyle:kTSMHiliteSelectedConvertedText atRange:range]; - [attrString setAttributes:attrs range:range]; + [_preeditString addAttributes:attrs range:range]; } if (MIN(NSMaxRange(range), pos) < preedit.length) { NSRange rawRange = NSMakeRange(MIN(NSMaxRange(range), pos), preedit.length - MIN(NSMaxRange(range), pos)); attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; - [attrString setAttributes:attrs range:rawRange]; + [_preeditString addAttributes:attrs range:rawRange]; } - [self.client setMarkedText:attrString - selectionRange:NSMakeRange(pos, 0) - replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + [self updateComposition]; } - (void)showPanelWithPreedit:(NSString *)preedit @@ -519,6 +556,8 @@ - (void)updateAppOptions NSLog(@"set app option: %@ = %d", key, value); rime_get_api()->set_option(_session, key.UTF8String, value); } + _panellessCommitFix = (appOptions[@"panelless_commit_fix"] ? : @(NO)).boolValue; + _inlinePlaceHolder = (appOptions[@"inline_placeholder"] ? : @(NO)).boolValue; } } @@ -537,7 +576,7 @@ - (BOOL)rimeConsumeCommittedText RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - if (_preeditString.length == 0) { + if (_preeditString.length == 0 && _panellessCommitFix) { [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; } [self commitString:commitText]; @@ -628,9 +667,11 @@ - (void)rimeUpdate // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. // note this is a full-shape space U+3000; using half shape characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") + selRange:NSMakeRange(0, 0) caretPos:0]; } } + // update candidates NSMutableArray *candidates = [NSMutableArray array]; NSMutableArray *comments = [NSMutableArray array]; diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 3168c6469..f11461131 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -345,6 +345,7 @@ app_options: com.apple.Terminal: ascii_mode: true no_inline: true + inline_placeholder: true com.googlecode.iterm2: ascii_mode: true no_inline: true @@ -354,6 +355,7 @@ app_options: vim_mode: true # 退出VIM插入模式自動切換輸入法狀態 com.apple.dt.Xcode: ascii_mode: true + no_inline: true com.barebones.textwrangler: ascii_mode: true com.macromates.TextMate.preview: @@ -371,9 +373,14 @@ app_options: no_inline: true co.zeit.hyper: ascii_mode: true + org.alacritty: + ascii_mode: true + vim_mode: true + panelless_commit_fix: true com.google.Chrome: # 規避 https://github.com/rime/squirrel/issues/435 inline: true + inline_placeholder: true ru.keepcoder.Telegram: # 規避 https://github.com/rime/squirrel/issues/475 inline: true diff --git a/librime b/librime index 8269def7d..897bc7e7e 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 8269def7daefcc24fb60b001377b0e58b197f4cf +Subproject commit 897bc7e7eca705d93d45d592c1ffa3ffbc1c246e From e78263b3f79e841498d8afb180ae1271e300abf9 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 18 Aug 2023 13:44:14 +0200 Subject: [PATCH 135/164] macOS 14 compatible --- Info.plist | 32 +- Squirrel.xcodeproj/project.pbxproj | 14 +- SquirrelInputController.m | 15 +- SquirrelPanel.m | 656 ++++++++++++++--------------- rime.pdf | Bin 47964 -> 51874 bytes rime.svg | 20 + 6 files changed, 379 insertions(+), 358 deletions(-) create mode 100644 rime.svg diff --git a/Info.plist b/Info.plist index 0d79502dd..adf633d4f 100644 --- a/Info.plist +++ b/Info.plist @@ -30,8 +30,10 @@ im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage zh-Hant + TISIconIsTemplate + tsInputModeAlternateMenuIconFileKey - rime.pdf + rime.svg tsInputModeCharacterRepertoireKey Hant @@ -44,9 +46,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.pdf + rime.svg tsInputModePaletteIconFileKey - rime.pdf + rime.svg tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -58,8 +60,10 @@ im.rime.inputmethod.Squirrel.Hans TISIntendedLanguage zh-Hans + TISIconIsTemplate + tsInputModeAlternateMenuIconFileKey - rime.pdf + rime.svg tsInputModeCharacterRepertoireKey Hans @@ -72,9 +76,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.pdf + rime.svg tsInputModePaletteIconFileKey - rime.pdf + rime.svg tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -86,8 +90,10 @@ im.rime.inputmethod.Squirrel.Cant TISIntendedLanguage yue-Hant + TISIconIsTemplate + tsInputModeAlternateMenuIconFileKey - rime.pdf + rime.svg tsInputModeCharacterRepertoireKey Hant @@ -100,9 +106,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.pdf + rime.svg tsInputModePaletteIconFileKey - rime.pdf + rime.svg tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -143,11 +149,13 @@ Hant Hans + TISIconIsTemplate + tsInputMethodIconFileKey - rime.pdf + rime.svg tsInputMethodAlternateIconFileKey - rime.pdf + rime.svg tsInputMethodPaletteIconFileKey - rime.pdf + rime.svg diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index c2e525315..ee6cd7f9b 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -83,7 +83,8 @@ A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; D26434552706A15100857391 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D26434542706A15100857391 /* QuartzCore.framework */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; - F4DEA7A62A8C1B8A002D4DAB /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */; }; + F4E409FC2A96C25D00A4C391 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F4E409FB2A96C25D00A4C391 /* rime.pdf */; }; + F4E7E0D12A9B19770040701F /* rime.svg in Resources */ = {isa = PBXBuildFile; fileRef = F4E7E0D02A9B19770040701F /* rime.svg */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -280,7 +281,8 @@ F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; - F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = rime.pdf; sourceTree = ""; }; + F4E409FB2A96C25D00A4C391 /* rime.pdf */ = {isa = PBXFileReference; explicitFileType = image.icns; path = rime.pdf; sourceTree = ""; }; + F4E7E0D02A9B19770040701F /* rime.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = rime.svg; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -366,7 +368,8 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( - F4DEA7A52A8C1B8A002D4DAB /* rime.pdf */, + F4E7E0D02A9B19770040701F /* rime.svg */, + F4E409FB2A96C25D00A4C391 /* rime.pdf */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, 44F7708E152B3334005CF491 /* dsa_pub.pem */, @@ -539,11 +542,12 @@ buildActionMask = 2147483647; files = ( 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + F4E7E0D12A9B19770040701F /* rime.svg in Resources */, A45578F51146A75200592C6E /* MainMenu.xib in Resources */, 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, - F4DEA7A62A8C1B8A002D4DAB /* rime.pdf in Resources */, + F4E409FC2A96C25D00A4C391 /* rime.pdf in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, ); @@ -718,6 +722,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = RimeIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -776,6 +781,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = RimeIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index f02fce8dc..b92eb05e0 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -593,7 +593,7 @@ - (void)rimeUpdate return; } - BOOL switcher = rime_get_api()->get_option(_session, "dumb"); + BOOL switcherMenu = rime_get_api()->get_option(_session, "dumb"); RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { @@ -608,7 +608,7 @@ - (void)rimeUpdate _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !switcher); + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !switcherMenu); } rime_get_api()->free_status(&status); } @@ -620,7 +620,7 @@ - (void)rimeUpdate NSString *preeditText = preedit ? @(preedit) : @""; // update composed string - if (!preedit || switcher) { + if (!preedit || switcherMenu) { _composedString = @""; } else if (rime_get_api()->get_option(_session, "soft_cursor")) { int cursorPos = ctx.composition.cursor_pos; @@ -640,7 +640,7 @@ - (void)rimeUpdate NSUInteger start = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; NSUInteger end = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; - if (_inlineCandidate && !switcher) { + if (_inlineCandidate && !switcherMenu) { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit) { @@ -661,7 +661,7 @@ - (void)rimeUpdate caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; } } else { - if (_inlinePreedit && !switcher) { + if (_inlinePreedit && !switcherMenu) { [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; } else { // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. @@ -671,7 +671,6 @@ - (void)rimeUpdate selRange:NSMakeRange(0, 0) caretPos:0]; } } - // update candidates NSMutableArray *candidates = [NSMutableArray array]; NSMutableArray *comments = [NSMutableArray array]; @@ -699,9 +698,9 @@ - (void)rimeUpdate [labels addObject:[labelString substringWithRange:NSMakeRange(i, 1)]]; } } - [self showPanelWithPreedit:(_inlinePreedit && !switcher ? nil : preeditText) + [self showPanelWithPreedit:(_inlinePreedit && !switcherMenu ? nil : preeditText) selRange:NSMakeRange(start, end - start) - caretPos:(switcher ? NSNotFound : caretPos) + caretPos:(switcherMenu ? NSNotFound : caretPos) candidates:candidates comments:comments labels:labels diff --git a/SquirrelPanel.m b/SquirrelPanel.m index c23abe578..a028616da 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -97,6 +97,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSDictionary *preeditHighlightedAttrs; @property(nonatomic, strong, readonly) NSDictionary *pagingAttrs; @property(nonatomic, strong, readonly) NSDictionary *pagingHighlightedAttrs; +@property(nonatomic, strong, readonly) NSDictionary *statusAttrs; @property(nonatomic, strong, readonly) NSParagraphStyle *paragraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *preeditParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; @@ -145,7 +146,8 @@ - (void) setAttrs:(NSMutableDictionary *)attrs preeditAttrs:(NSMutableDictionary *)preeditAttrs preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs; + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs + statusAttrs:(NSMutableDictionary *)statusAttrs; - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle @@ -243,7 +245,8 @@ - (void) setAttrs:(NSMutableDictionary *)attrs preeditAttrs:(NSMutableDictionary *)preeditAttrs preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs { + pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs + statusAttrs:(NSMutableDictionary *)statusAttrs { _attrs = attrs; _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; @@ -254,26 +257,41 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _preeditHighlightedAttrs = preeditHighlightedAttrs; _pagingAttrs = pagingAttrs; _pagingHighlightedAttrs = pagingHighlightedAttrs; - _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" - attributes:@{NSGlyphInfoAttributeName: - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" - forFont:_pagingAttrs[NSFontAttributeName] - baseString:@"◀"]}]; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" - attributes:@{NSGlyphInfoAttributeName: - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" - forFont:_pagingAttrs[NSFontAttributeName] - baseString:@"◁"]}]; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" - attributes:@{NSGlyphInfoAttributeName: - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" - forFont:_pagingAttrs[NSFontAttributeName] - baseString:@"▶"]}]; - _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" - attributes:@{NSGlyphInfoAttributeName: - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" - forFont:_pagingAttrs[NSFontAttributeName] - baseString:@"▷"]}]; + _statusAttrs = statusAttrs; + NSMutableDictionary *symbolAttrs = [pagingAttrs mutableCopy]; + if (@available(macOS 12.0, *)) { + symbolAttrs[NSVerticalGlyphFormAttributeName] = @(_vertical && _linear); + + _symbolBackFill = [[NSAttributedString alloc] + initWithString:(_vertical && _linear ? @"􀁧" : @"􀁫") attributes:symbolAttrs]; + _symbolBackStroke = [[NSAttributedString alloc] + initWithString:(_vertical && _linear ? @"􀁦" : @"􀁪") attributes:symbolAttrs]; + _symbolForwardFill = [[NSAttributedString alloc] + initWithString:(_vertical && _linear ? @"􀁩" : @"􀁭") attributes:symbolAttrs]; + _symbolForwardStroke = [[NSAttributedString alloc] + initWithString:(_vertical && _linear ? @"􀁨" : @"􀁬") attributes:symbolAttrs]; + } else { + NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] + fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] + size:[labelAttrs[NSFontAttributeName] pointSize]]; + symbolAttrs[NSFontAttributeName] = symbolFont; + + NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; + symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀\uFE0E"]; + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀\uFE0E" attributes:symbolAttrsBackFill]; + + NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; + symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁\uFE0E"]; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁\uFE0E" attributes:symbolAttrsBackStroke]; + + NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶\uFE0E"]; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶\uFE0E" attributes:symbolAttrsForwardFill]; + + NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷\uFE0E"]; + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷\uFE0E" attributes:symbolAttrsForwardStroke]; + } } - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -317,10 +335,6 @@ - (void) drawViewWithInsets:(NSEdgeInsets)insets pagingRange:(NSRange)pagingRange pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; -- (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPointPointer)origin - withReferenceFont:(NSFont *)refFont - paragraphStyle:(NSParagraphStyle *)style; @end @@ -416,92 +430,16 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) } } -- (NSRect)setLineRectForRange:(NSRange)charRange - atOrigin:(NSPointPointer)origin - withReferenceFont:(NSFont *)refFont - paragraphStyle:(NSParagraphStyle *)style { - NSLayoutManager *layoutManager = _textView.layoutManager; - NSTextContainer *textContainer = _textView.textContainer; - BOOL verticalLayout = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat refBaseline = refFont.ascender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; - lineHeight += style.lineSpacing; - - NSRect blockRect = NSZeroRect; - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSUInteger i = glyphRange.location; - NSRange lineRange = NSMakeRange(i, 0); - while (i < NSMaxRange(glyphRange)) { - // typsetting the line fragment - NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; - NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; - NSRange lineCharRange = [layoutManager characterRangeForGlyphRange:lineRange actualGlyphRange:NULL]; - rect.origin.y = origin->y; - usedRect.origin.y = origin->y; - rect.size.height = lineHeight; - usedRect.size.height = MAX(NSHeight(usedRect), lineHeight); - CGFloat alignment = verticalLayout ? lineHeight / 2 : refBaseline + MAX(0.0, lineHeight - refFontHeight) / 2; - if (style.paragraphSpacing > 0 && NSMaxRange(lineCharRange) != _textStorage.length && - [_textStorage.string characterAtIndex:NSMaxRange(lineCharRange) - 1] == '\n') { - rect.size.height += style.paragraphSpacing; - } - if (style.paragraphSpacingBefore > 0 && lineCharRange.location != 0 && - [_textStorage.string characterAtIndex:lineCharRange.location - 1] == '\n') { - rect.size.height += style.paragraphSpacingBefore; - usedRect.origin.y += style.paragraphSpacingBefore; - alignment += style.paragraphSpacingBefore; - } - usedRect = NSIntersectionRect(usedRect, rect); - [layoutManager setLineFragmentRect:rect forGlyphRange:lineRange usedRect:usedRect]; - blockRect = NSUnionRect(blockRect, rect); - origin->y = NSMaxY(rect); - - // typesetting glyphs - NSRange fontRunRange = NSMakeRange(NSNotFound, 0); - NSUInteger j = lineRange.location; - while (j < NSMaxRange(lineRange)) { - NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; - NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; - NSFont *runFont = [_textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; - NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; - NSNumber *baselineOffset = [_textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL]; - CGFloat offset = (baselineOffset == nil) ? 0.0 : baselineOffset.doubleValue; - NSRange fontRunGlyphRange = [layoutManager glyphRangeForCharacterRange:fontRunRange actualCharacterRange:NULL]; - NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange)-j), [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); - if (verticalLayout) { - runFont = runFont.verticalFont; - resizedRefFont = resizedRefFont.verticalFont; - } - CGFloat runBaseline = runFont.ascender; - CGFloat runFontHeight = runFont.ascender - runFont.descender; - CGFloat resizedRefFontHeight = resizedRefFont.ascender - resizedRefFont.descender; - if (verticalLayout) { - runGlyphPosition.y = alignment - offset + ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/4); - if (runFont.isVertical) { - runGlyphPosition.x += ceil(MAX(0.0, runFontHeight - resizedRefFontHeight)/2); - } else { - runGlyphPosition.y += runBaseline - runFontHeight/2; - } - } else { - runGlyphPosition.y = alignment - offset; - } - [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; - j = NSMaxRange(runRange); - } - i = NSMaxRange(lineRange); - } - return blockRect; -} // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { if (@available(macOS 12.0, *)) { [_textView.textLayoutManager ensureLayoutForRange:_textView.textContentStorage.documentRange]; - return NSInsetRect([_textView.textLayoutManager usageBoundsForTextContainer], - -_textView.textContainer.lineFragmentPadding, 0); + NSRect rect = [_textView.textLayoutManager usageBoundsForTextContainer]; + if (@available(macOS 14.0, *)) { // lineFragments no longer include Paddings as of macOS 14 + rect = NSInsetRect(rect, -_textView.textContainer.lineFragmentPadding, 0); + } + return rect; } else { [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; return [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; @@ -513,12 +451,12 @@ - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:range]; __block NSRect contentRect = NSZeroRect; - [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { - contentRect = NSUnionRect(contentRect, segmentRect); + [_textView.textLayoutManager + enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + contentRect = NSUnionRect(contentRect, segFrame); return YES; }]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:NSMaxRange(range)-1 effectiveRange:NULL] lineSpacing]; @@ -564,8 +502,8 @@ - (void) drawViewWithInsets:(NSEdgeInsets)insets _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; _pagingButton = pagingButton; - _candidatePaths = [NSMutableArray arrayWithCapacity:candidateRanges.count]; - _pagingPaths = [NSMutableArray arrayWithCapacity:pagingRange.length > 0 ? 2 : 0]; + _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; + _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; self.needsDisplay = YES; } @@ -631,17 +569,17 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromRange:charRange]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:charRange.location effectiveRange:NULL] lineSpacing]; - NSMutableArray *lineRects = [NSMutableArray arrayWithCapacity:2]; - NSMutableArray *lineRanges = [NSMutableArray arrayWithCapacity:2]; - [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded - usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentRect, CGFloat baselinePosition, NSTextContainer *textContainer) { - if (!nearEmptyRect(segmentRect)) { - segmentRect.size.height += lineSpacing; - [lineRects addObject:[NSValue valueWithRect:segmentRect]]; - [lineRanges addObject:segmentRange]; + NSMutableArray *lineRects = [[NSMutableArray alloc] initWithCapacity:2]; + NSMutableArray *lineRanges = [[NSMutableArray alloc] initWithCapacity:2]; + [_textView.textLayoutManager + enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + if (!nearEmptyRect(segFrame)) { + segFrame.size.height += lineSpacing; + [lineRects addObject:[NSValue valueWithRect:segFrame]]; + [lineRanges addObject:segRange]; } return YES; }]; @@ -814,65 +752,23 @@ - (void)updateLayer { NSRect preeditRect = NSZeroRect; NSRect candidateBlockRect = NSZeroRect; NSRect pagingLineRect = NSZeroRect; - if (@available(macOS 12.0, *)) { - if (preeditRange.length > 0) { - preeditRect = [self contentRectForRange:preeditRange]; - if (candidateBlockRange.length > 0) { - preeditRect.size.height += theme.preeditLinespace; - } - } - if (candidateBlockRange.length > 0) { - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); - if (preeditRange.length == 0) { - candidateBlockRect.origin.y += theme.linespace/2; - } - } else if (preeditRange.length == 0) { // status message - candidateBlockRect = [self contentRectForRange:visibleRange]; - } - if (!theme.linear && pagingRange.length > 0) { - pagingLineRect = [self contentRectForRange:pagingRange]; - pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; - pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; - } - } else { - NSPoint lineOrigin = NSZeroPoint; - CGFloat refFontSize = _preeditRange.length == 0 && _candidateRanges.count == 0 - ? [theme.commentAttrs[NSFontAttributeName] pointSize] - : [theme.attrs[NSFontAttributeName] pointSize]; - NSFont *refFont = CFBridgingRelease(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, refFontSize, (CFStringRef)@"zh")); - if (theme.vertical) { - refFont = refFont.verticalFont; - } - if (preeditRange.length > 0) { - preeditRect = [self setLineRectForRange:preeditRange - atOrigin:&lineOrigin - withReferenceFont:refFont - paragraphStyle:theme.preeditParagraphStyle]; - } + if (preeditRange.length > 0) { + preeditRect = [self contentRectForRange:preeditRange]; if (candidateBlockRange.length > 0) { - candidateBlockRect = [self setLineRectForRange:candidateBlockRange - atOrigin:&lineOrigin - withReferenceFont:refFont - paragraphStyle:theme.paragraphStyle]; - if (preeditRange.length == 0) { - candidateBlockRect.size.height += theme.linespace/2; - } - if (theme.linear || pagingRange.length == 0) { - candidateBlockRect.size.height += theme.linespace/2; - } - } else if (preeditRange.length == 0) { // status message - candidateBlockRect = [self setLineRectForRange:visibleRange - atOrigin:&lineOrigin - withReferenceFont:refFont - paragraphStyle:theme.statusParagraphStyle]; + preeditRect.size.height += theme.preeditLinespace; } - if (!theme.linear && pagingRange.length > 0) { - pagingLineRect = [self setLineRectForRange:pagingRange - atOrigin:&lineOrigin - withReferenceFont:theme.pagingAttrs[NSFontAttributeName] - paragraphStyle:theme.pagingParagraphStyle]; + } + if (candidateBlockRange.length > 0) { + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); + if (preeditRange.length == 0) { + candidateBlockRect.origin.y += theme.linespace/2; } } + if (!theme.linear && pagingRange.length > 0) { + pagingLineRect = [self contentRectForRange:pagingRange]; + pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; + pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; + } [NSBezierPath setDefaultLineWidth:0]; // Draw preedit Rect @@ -896,15 +792,15 @@ - (void)updateLayer { : NSIntersectionRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSIntersectionRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); - NSMutableArray *highlightedPreeditPoints; - NSMutableArray *highlightedPreeditPoints2; + NSArray *highlightedPreeditPoints; + NSArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPoints = [rectVertex(leadingRect) mutableCopy]; - highlightedPreeditPoints2 = [rectVertex(trailingRect) mutableCopy]; + highlightedPreeditPoints = rectVertex(leadingRect); + highlightedPreeditPoints2 = rectVertex(trailingRect); } else { - highlightedPreeditPoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + highlightedPreeditPoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight/3)); if (highlightedPreeditPoints2.count > 0) { @@ -962,15 +858,15 @@ - (void)updateLayer { } bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); } - NSMutableArray *candidatePoints; - NSMutableArray *candidatePoints2; + NSArray *candidatePoints; + NSArray *candidatePoints2; // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePoints = [rectVertex(leadingRect) mutableCopy]; - candidatePoints2 = [rectVertex(trailingRect) mutableCopy]; + candidatePoints = rectVertex(leadingRect); + candidatePoints2 = rectVertex(trailingRect); } else { - candidatePoints = [multilineRectVertex(leadingRect, bodyRect, trailingRect) mutableCopy]; + candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); if (candidatePoints2.count > 0) { @@ -986,7 +882,7 @@ - (void)updateLayer { candidateRect.origin.y += theme.linespace / 2; } candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); - NSMutableArray *candidatePoints = [rectVertex(candidateRect) mutableCopy]; + NSArray *candidatePoints = rectVertex(candidateRect); NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); _candidatePaths[i] = candidatePath; } @@ -996,10 +892,10 @@ - (void)updateLayer { // Draw paging Rect if (pagingRange.length > 0) { CGFloat buttonPadding = theme.linear ? MIN(_separatorWidth / 2, linePadding) : linePadding; - NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)], + NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 2, 2)], textContainerRect.origin.x, textContainerRect.origin.y); pageDownRect.size.width += buttonPadding; - NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 1)], + NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 2)], textContainerRect.origin.x, textContainerRect.origin.y); pageUpRect.origin.x -= buttonPadding; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented @@ -1042,29 +938,41 @@ - (void)updateLayer { textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3); [self.layer setSublayers:nil]; self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); - if (theme.backgroundImage) { - CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; - backgroundLayer.path = [backgroundPath quartzPath]; - backgroundLayer.fillColor = [theme.backgroundImage CGColor]; - backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); - [self.layer addSublayer:backgroundLayer]; - } - CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; - panelLayer.path = [textContainerPath quartzPath]; - panelLayer.fillColor = [theme.backgroundColor CGColor]; + CALayer *panelLayer = [[CALayer alloc] init]; [self.layer addSublayer:panelLayer]; + if (theme.backgroundImage) { + CAShapeLayer *backgroundImageLayer = [[CAShapeLayer alloc] init]; + if (theme.vertical) { + const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI/2); + backgroundImageLayer.path = CGPathCreateCopyByTransformingPath([textContainerPath quartzPath], &rotate); + backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; + [backgroundImageLayer setAffineTransform:CGAffineTransformMakeRotation(M_PI/2)]; + } else { + backgroundImageLayer.path = [textContainerPath quartzPath]; + backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; + } + backgroundImageLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + [panelLayer addSublayer:backgroundImageLayer]; + } + CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; + backgroundLayer.path = [textContainerPath quartzPath]; + backgroundLayer.fillColor = [theme.backgroundColor CGColor]; + backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + [panelLayer addSublayer:backgroundLayer]; if (theme.preeditBackgroundColor && (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { - panelLayer.fillColor = [theme.preeditBackgroundColor CGColor]; + backgroundLayer.fillColor = [theme.preeditBackgroundColor CGColor]; if (!candidateBlockPath.empty) { + [textContainerPath appendBezierPath:candidateBlockPath]; + textContainerPath.windingRule = NSEvenOddWindingRule; + backgroundLayer.path = [textContainerPath quartzPath]; + backgroundLayer.fillRule = kCAFillRuleEvenOdd; CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; candidateLayer.path = [candidateBlockPath quartzPath]; candidateLayer.fillColor = [theme.backgroundColor CGColor]; [panelLayer addSublayer:candidateLayer]; } } - CIFilter *backColorFilter = [CIFilter filterWithName:@"CISourceATopCompositing"]; - panelLayer.compositingFilter = backColorFilter; if (@available(macOS 10.14, *)) { if (theme.translucency > 0) { panelLayer.opacity = 1.0 - theme.translucency; @@ -1121,8 +1029,6 @@ - (void)updateLayer { borderLayer.fillRule = kCAFillRuleEvenOdd; [panelLayer addSublayer:borderLayer]; } - - [_textView display]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { @@ -1221,10 +1127,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { size:kDefaultFontSize]; NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; - NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] - fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:kDefaultFontSize]; NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; + defaultAttrs[NSStrokeWidthAttributeName] = @(-1.0); NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; @@ -1262,12 +1166,10 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *pagingAttrs = [defaultAttrs mutableCopy]; pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? accentColor : [NSColor controlTextColor]; - pagingAttrs[NSFontAttributeName] = symbolFont; NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] - : [NSColor selectedMenuItemTextColor]; - pagingHighlightedAttrs[NSFontAttributeName] = symbolFont; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear + ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; @@ -1287,6 +1189,12 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + NSMutableDictionary *statusAttrs = [commentAttrs mutableCopy]; + statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; + + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + [theme setAttrs:attrs highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs @@ -1296,7 +1204,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { preeditAttrs:preeditAttrs preeditHighlightedAttrs:preeditHighlightedAttrs pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs]; + pagingHighlightedAttrs:pagingHighlightedAttrs + statusAttrs:statusAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle pagingParagraphStyle:pagingParagraphStyle @@ -1579,14 +1488,10 @@ - (void)show { [self.contentView setBoundsRotation:0.0]; [self.contentView setBoundsOrigin:NSZeroPoint]; } - NSRect frameRect = [self.contentView backingAlignedRect:self.contentView.bounds - options:NSAlignMinXInward|NSAlignMaxYOutward|NSAlignWidthNearest|NSAlignHeightNearest]; + NSRect frameRect = self.contentView.bounds; NSRect textFrameRect = NSMakeRect(NSMinX(frameRect) + insets.left, NSMinY(frameRect) + insets.bottom, NSWidth(frameRect) - insets.left - insets.right, NSHeight(frameRect) - insets.top - insets.bottom); - if (@available(macOS 12.0, *)) { - textFrameRect = NSInsetRect(textFrameRect, linePadding, 0); - } [_view.textView setBoundsRotation:0.0]; [_view setBoundsOrigin:NSZeroPoint]; [_view.textView setBoundsOrigin:NSZeroPoint]; @@ -1617,15 +1522,78 @@ - (void)hide { _maxSize = NSZeroSize; } +- (void)setLayoutForRange:(NSRange)charRange + withReferenceFont:(NSFont *)refFont + paragraphStyle:(NSParagraphStyle *)style { + BOOL verticalLayout = _view.currentTheme.vertical; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + if (@available(macOS 12.0, *)) { + NSUInteger i = charRange.location; + NSRange runRange = NSMakeRange(i, 0); + while (i < NSMaxRange(charRange)) { + CGFloat baselineOffset = [[_view.textStorage attribute:NSBaselineOffsetAttributeName atIndex:i effectiveRange:&runRange] doubleValue]; + [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset + lineHeight/2 - refFontHeight/2) range:runRange]; + i = NSMaxRange(runRange); + } + } else { + NSLayoutManager *layoutManager = _view.textView.layoutManager; + NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; + NSUInteger i = glyphRange.location; + NSRange lineRange = NSMakeRange(i, 0); + while (i < NSMaxRange(glyphRange)) { + NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; + NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; + CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight/2 + refFont.descender + refFontHeight/2); + // typesetting glyphs + NSRange fontRunRange = NSMakeRange(NSNotFound, 0); + NSUInteger j = lineRange.location; + while (j < NSMaxRange(lineRange)) { + NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; + NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; + NSFont *runFont = [_view.textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; + NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; + NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL]; + CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; + NSRange fontRunGlyphRange = [layoutManager glyphRangeForCharacterRange:fontRunRange actualCharacterRange:NULL]; + NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange)-j), [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + if (verticalLayout) { + NSNumber *verticalGlyph = [_view.textStorage attribute:NSVerticalGlyphFormAttributeName atIndex:runCharLocation effectiveRange:NULL]; + if (verticalGlyph ? verticalGlyph.boolValue : YES) { + runFont = runFont.verticalFont; + systemFont = systemFont.verticalFont; + } + } + CGFloat runBaseline = runFont.ascender; + CGFloat runFontHeight = runFont.ascender - runFont.descender; + CGFloat systemFontHeight = systemFont.ascender - systemFont.descender; + if (verticalLayout) { + runGlyphPosition.y = alignment - offset + runBaseline - runFontHeight/2 + round(MAX(0.0, runFontHeight - systemFontHeight) / 3); + if (runFont.vertical) { + runGlyphPosition.x += round(MAX(0.0, runFontHeight - systemFontHeight) * 2/3); + } + } else { + runGlyphPosition.y = alignment - offset; + } + [layoutManager setLocation:NSMakePoint(round(runGlyphPosition.x), round(runGlyphPosition.y)) forStartOfGlyphRange:runRange]; + j = NSMaxRange(runRange); + } + i = NSMaxRange(lineRange); + } + } +} + - (BOOL)shouldBreakLineWithRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; NSUInteger __block lineCount = 0; - [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { + [_view.textView.textLayoutManager + enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { ++lineCount; return YES; }]; @@ -1650,15 +1618,21 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; CGFloat __block rangeEdge; - [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock: - ^(NSTextRange *segmentRange, CGRect segmentFrame, CGFloat baselinePosition, NSTextContainer *textContainer) { - rangeEdge = NSMaxX(segmentFrame); + [_view.textView.textLayoutManager + enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + rangeEdge = NSMaxX(segFrame); return YES; }]; - NSRect container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; + NSRect container; + if (@available(macOS 14.0, *)) { + container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; + } else { + container = NSInsetRect([_view.textView.textLayoutManager usageBoundsForTextContainer], + _view.textView.textContainer.lineFragmentPadding, 0); + } *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } else { @@ -1730,7 +1704,7 @@ - (void)showPreedit:(NSString *)preedit _maxSize.width = MIN(floor(theme.lineLength), _textWidthLimit); } NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, - theme.edgeInset.height + theme.linespace / 2 + theme.paragraphStyle.lineSpacing, theme.edgeInset.width); + theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width); NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; @@ -1763,16 +1737,12 @@ - (void)showPreedit:(NSString *)preedit value:@NO range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } - [preeditLine addAttribute:NSParagraphStyleAttributeName - value:theme.preeditParagraphStyle - range:NSMakeRange(0, preeditLine.length)]; - preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { - insets.bottom = theme.edgeInset.height + theme.preeditParagraphStyle.lineSpacing; + insets.bottom = theme.edgeInset.height; } insets.top = theme.edgeInset.height; } @@ -1865,18 +1835,20 @@ - (void)showPreedit:(NSString *)preedit if (numCandidates > 0 && theme.showPaging) { NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; - NSMutableAttributedString *pageUpString = [(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke) mutableCopy]; - [pageUpString addAttributes:(_turnPage == NSPageUpFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs) - range:NSMakeRange(0, pageUpString.length)]; + NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] initWithAttributedString:(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke)]; + if (_turnPage == NSPageUpFunctionKey) { + [pageUpString addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(0, pageUpString.length)]; + } [paging appendAttributedString:pageUpString]; - [paging appendAttributedString:[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] - attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; - NSMutableAttributedString *pageDownString = [(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill) mutableCopy]; - [pageDownString addAttributes:(_turnPage == NSPageDownFunctionKey ? theme.pagingHighlightedAttrs : theme.pagingAttrs) - range:NSMakeRange(0, pageDownString.length)]; + NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]; + if (_turnPage == NSPageDownFunctionKey) { + [pageDownString addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(0, pageDownString.length)]; + } [paging appendAttributedString:pageDownString]; [text appendAttributedString:[[NSAttributedString alloc] @@ -1905,8 +1877,8 @@ - (void)showPreedit:(NSString *)preedit } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { - [paging replaceCharactersInRange:NSMakeRange(1, 1) withString:@"\t"]; - [paging replaceCharactersInRange:NSMakeRange(paging.length - 2, 1) withString:@"\t"]; + [paging replaceCharactersInRange:NSMakeRange(pageUpString.length, 1) withString:@"\t"]; + [paging replaceCharactersInRange:NSMakeRange(paging.length - pageDownString.length - 1, 1) withString:@"\t"]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength/2], [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; } @@ -1919,7 +1891,26 @@ - (void)showPreedit:(NSString *)preedit pagingRange = NSMakeRange(text.length - paging.length, paging.length); } + [text fixAttributesInRange:NSMakeRange(0, text.length)]; + if (preedit) { + [self setLayoutForRange:preeditRange + withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) + paragraphStyle:theme.preeditParagraphStyle]; + } + if (numCandidates > 0) { + NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); + [self setLayoutForRange:candidateBlockRange + withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) + paragraphStyle:theme.paragraphStyle]; + if (!theme.linear && pagingRange.length > 0) { + [self setLayoutForRange:pagingRange + withReferenceFont:theme.pagingAttrs[NSFontAttributeName] + paragraphStyle:theme.pagingParagraphStyle]; + } + } + // text done! + [self setAnimationBehavior:caretPos == NSNotFound ? NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; [_view drawViewWithInsets:insets candidateRanges:candidateRanges highlightedIndex:index @@ -1927,7 +1918,6 @@ - (void)showPreedit:(NSString *)preedit highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange pagingButton:turnPage]; - [self show]; } @@ -1955,20 +1945,23 @@ - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width, - theme.edgeInset.height + theme.statusParagraphStyle.lineSpacing, theme.edgeInset.width); + theme.edgeInset.height, theme.edgeInset.width); _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; - [text appendAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.commentAttrs]]; - [text addAttribute:NSParagraphStyleAttributeName - value:theme.statusParagraphStyle - range:NSMakeRange(0, text.length)]; + [text appendAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; + + [text fixAttributesInRange:NSMakeRange(0, text.length)]; + [self setLayoutForRange:NSMakeRange(0, text.length) + withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) + paragraphStyle:theme.statusParagraphStyle]; if (_statusTimer) { [_statusTimer invalidate]; } + [self setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow]; [_view drawViewWithInsets:insets candidateRanges:@[] highlightedIndex:NSNotFound @@ -2016,7 +2009,7 @@ - (void)hideStatus:(NSTimer *)timer { } NSArray *fontNames = [fullname componentsSeparatedByString:@","]; - NSMutableArray *validFontDescriptors = [NSMutableArray arrayWithCapacity:fontNames.count]; + NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] initWithCapacity:fontNames.count]; for (__strong NSString *fontName in fontNames) { fontName = [fontName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; if ([NSFont fontWithName:fontName size:0.0] != nil) { @@ -2042,20 +2035,18 @@ - (void)hideStatus:(NSTimer *)timer { return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } -static CGFloat getLineHeight(NSFont *font, BOOL vertical, BOOL includeFallback) { +static CGFloat getLineHeight(NSFont *font, BOOL vertical) { if (vertical) { font = font.verticalFont; } - CGFloat lineHeight = ceil(font.ascender - font.descender); - if (includeFallback) { - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; - for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; - if (vertical) { - fallbackFont = fallbackFont.verticalFont; - } - lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); + CGFloat lineHeight = font.ascender - font.descender; + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; } + lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); } return lineHeight; } @@ -2278,12 +2269,12 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } } - fontSize = fontSize ? fontSize : kDefaultFontSize; - labelFontSize = labelFontSize ? labelFontSize : fontSize; - commentFontSize = commentFontSize ? commentFontSize : fontSize; + fontSize = fontSize ? : kDefaultFontSize; + labelFontSize = labelFontSize ? : fontSize; + commentFontSize = commentFontSize ? : fontSize; NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); - NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? fontDescriptor : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) + NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) size:fontSize]; NSFontDescriptor *labelFontDescriptor = getFontDescriptor(labelFontName); @@ -2292,44 +2283,37 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); - NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? commentFontDescriptor : fontDescriptor) + NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) size:commentFontSize]; NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:labelFontSize]; - CGFloat fontHeight = getLineHeight(font, vertical, NO); - CGFloat fontHeightMax = getLineHeight(font, vertical, YES); - CGFloat labelFontHeight = getLineHeight(labelFont, vertical, NO); - CGFloat labelFontHeightMax = getLineHeight(labelFont, vertical, YES); - CGFloat commentFontHeight = getLineHeight(commentFont, vertical, NO); - CGFloat commentFontHeightMax = getLineHeight(commentFont, vertical, YES); + CGFloat fontHeight = getLineHeight(font, vertical); + CGFloat labelFontHeight = getLineHeight(labelFont, vertical); + CGFloat commentFontHeight = getLineHeight(commentFont, vertical); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); - CGFloat lineHeightMax = MAX(fontHeightMax, MAX(labelFontHeightMax, commentFontHeightMax)); NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; - preeditParagraphStyle.minimumLineHeight = (fontHeight + fontHeightMax) / 2; - preeditParagraphStyle.maximumLineHeight = (fontHeight + fontHeightMax) / 2; - preeditParagraphStyle.lineSpacing = (fontHeightMax - fontHeight) / 2; + preeditParagraphStyle.minimumLineHeight = fontHeight; + preeditParagraphStyle.maximumLineHeight = fontHeight; preeditParagraphStyle.paragraphSpacing = spacing; NSMutableParagraphStyle *paragraphStyle = [theme.paragraphStyle mutableCopy]; - paragraphStyle.minimumLineHeight = (lineHeight + lineHeightMax) / 2; - paragraphStyle.maximumLineHeight = (lineHeight + lineHeightMax) / 2; - paragraphStyle.lineSpacing = (lineHeightMax - lineHeight) / 2; + paragraphStyle.minimumLineHeight = lineHeight; + paragraphStyle.maximumLineHeight = lineHeight; paragraphStyle.paragraphSpacing = lineSpacing / 2; paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; - pagingParagraphStyle.minimumLineHeight = getLineHeight(pagingFont, NO, NO); - pagingParagraphStyle.maximumLineHeight = getLineHeight(pagingFont, NO, NO); + pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; + pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; pagingParagraphStyle.paragraphSpacingBefore = pagingFont.leading; NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; - statusParagraphStyle.minimumLineHeight = (commentFontHeight + commentFontHeightMax) / 2; - statusParagraphStyle.maximumLineHeight = (commentFontHeight + commentFontHeightMax) / 2; - statusParagraphStyle.lineSpacing = (commentFontHeightMax - commentFontHeight) / 2; + statusParagraphStyle.minimumLineHeight = commentFontHeight; + statusParagraphStyle.maximumLineHeight = commentFontHeight; NSMutableDictionary *attrs = [theme.attrs mutableCopy]; NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; @@ -2341,6 +2325,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSMutableDictionary *preeditHighlightedAttrs = [theme.preeditHighlightedAttrs mutableCopy]; NSMutableDictionary *pagingAttrs = [theme.pagingAttrs mutableCopy]; NSMutableDictionary *pagingHighlightedAttrs = [theme.pagingHighlightedAttrs mutableCopy]; + NSMutableDictionary *statusAttrs = [theme.statusAttrs mutableCopy]; attrs[NSFontAttributeName] = font; highlightedAttrs[NSFontAttributeName] = font; @@ -2350,21 +2335,20 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f commentHighlightedAttrs[NSFontAttributeName] = commentFont; preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; - pagingAttrs[NSFontAttributeName] = pagingFont; - pagingHighlightedAttrs[NSFontAttributeName] = pagingFont; + pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; + statusAttrs[NSFontAttributeName] = commentFont; - CGFloat labelBaselineShift = vertical && labelFont.verticalFont.isVertical ? 0.0 - : MAX(font.ascender, MAX(labelFont.ascender, commentFont.ascender)) - lineHeight/2 - labelFont.ascender + labelFontHeight/2; + CGFloat labelMidlineOffset = vertical ? 0.0 : font.ascender/2 + font.descender/2 - labelFont.ascender/2 - labelFont.descender/2; attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelBaselineShift); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelBaselineShift); + labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelMidlineOffset); + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelMidlineOffset); commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + statusAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); NSColor *secondaryTextColor = [[self class] secondaryTextColor]; NSColor *accentColor = [[self class] accentColor]; @@ -2389,19 +2373,19 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f } } - backgroundColor = backgroundColor ? backgroundColor : [NSColor controlBackgroundColor]; - borderColor = borderColor ? borderColor : isNative ? [NSColor gridColor] : nil; - preeditBackgroundColor = preeditBackgroundColor ? preeditBackgroundColor : isNative ? [NSColor windowBackgroundColor] : nil; - candidateTextColor = candidateTextColor ? candidateTextColor : [NSColor controlTextColor]; - highlightedCandidateTextColor = highlightedCandidateTextColor ? highlightedCandidateTextColor : [NSColor selectedMenuItemTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? highlightedCandidateBackColor : isNative ? [NSColor alternateSelectedControlColor] : nil; - candidateLabelColor = candidateLabelColor ? candidateLabelColor : isNative ? accentColor : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); - highlightedCandidateLabelColor = highlightedCandidateLabelColor ? highlightedCandidateLabelColor : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); - commentTextColor = commentTextColor ? commentTextColor : secondaryTextColor; - highlightedCommentTextColor = highlightedCommentTextColor ? highlightedCommentTextColor : [NSColor alternateSelectedControlTextColor]; + backgroundColor = backgroundColor ? : [NSColor controlBackgroundColor]; + borderColor = borderColor ? : isNative ? [NSColor gridColor] : nil; + preeditBackgroundColor = preeditBackgroundColor ? : isNative ? [NSColor windowBackgroundColor] : nil; + candidateTextColor = candidateTextColor ? : [NSColor controlTextColor]; + highlightedCandidateTextColor = highlightedCandidateTextColor ? : [NSColor selectedMenuItemTextColor]; + highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? [NSColor alternateSelectedControlColor] : nil; + candidateLabelColor = candidateLabelColor ? : isNative ? accentColor : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); + highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); + commentTextColor = commentTextColor ? : secondaryTextColor; + highlightedCommentTextColor = highlightedCommentTextColor ? : [NSColor alternateSelectedControlTextColor]; textColor = textColor ? textColor : [NSColor textColor]; - highlightedTextColor = highlightedTextColor ? highlightedTextColor : [NSColor selectedTextColor]; - highlightedBackColor = highlightedBackColor ? highlightedBackColor : isNative ? [NSColor selectedTextBackgroundColor] : nil; + highlightedTextColor = highlightedTextColor ? : [NSColor selectedTextColor]; + highlightedBackColor = highlightedBackColor ? : isNative ? [NSColor selectedTextBackgroundColor] : nil; attrs[NSForegroundColorAttributeName] = candidateTextColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; @@ -2411,14 +2395,37 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f commentHighlightedAttrs[NSForegroundColorAttributeName] = highlightedCommentTextColor; preeditAttrs[NSForegroundColorAttributeName] = textColor; preeditHighlightedAttrs[NSForegroundColorAttributeName] = highlightedTextColor; - pagingAttrs[NSForegroundColorAttributeName] = linear ? candidateLabelColor : candidateTextColor; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedCandidateTextColor; + pagingAttrs[NSForegroundColorAttributeName] = linear ? candidateLabelColor : textColor; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedTextColor; + statusAttrs[NSForegroundColorAttributeName] = commentTextColor; + + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); - pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(NO); + NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); + + [theme setCornerRadius:MIN(cornerRadius, lineHeight/2) + hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/3) + edgeInset:edgeInset + linespace:lineSpacing + preeditLinespace:spacing + alpha:(alpha == 0 ? 1.0 : alpha) + translucency:translucency + lineLength:lineLength + showPaging:showPaging + rememberSize:rememberSize + linear:linear + vertical:vertical + inlinePreedit:inlinePreedit + inlineCandidate:inlineCandidate]; + + theme.native = isNative; + [theme setCandidateFormat:candidateFormat ? candidateFormat : kDefaultCandidateFormat]; [theme setStatusMessageType:statusMessageType]; [theme setAttrs:attrs @@ -2430,7 +2437,8 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f preeditAttrs:preeditAttrs preeditHighlightedAttrs:preeditHighlightedAttrs pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs]; + pagingHighlightedAttrs:pagingHighlightedAttrs + statusAttrs:statusAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle @@ -2443,26 +2451,6 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; - - NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - - [theme setCornerRadius:MIN(cornerRadius, lineHeightMax/2) - hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeightMax/3) - edgeInset:edgeInset - linespace:lineSpacing - preeditLinespace:spacing - alpha:(alpha == 0 ? 1.0 : alpha) - translucency:translucency - lineLength:lineLength - showPaging:showPaging - rememberSize:rememberSize - linear:linear - vertical:vertical - inlinePreedit:inlinePreedit - inlineCandidate:inlineCandidate]; - - theme.native = isNative; - theme.candidateFormat = candidateFormat ? candidateFormat : kDefaultCandidateFormat; } @end diff --git a/rime.pdf b/rime.pdf index 89a2f8ab114e0f5bdde16494f8949846a4c52cd5..7145edc7406a705cb04280df7c945cf82128dde6 100644 GIT binary patch delta 41088 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 ?$ zrz^*m1O{kA>RyI4w@&RiVZ{{psDS*j9XT;>LbLW^-U|8T_Uhr0W15G13@etWj&1e~ zf;1dIz2vgohPt}NF=bt0W#6h5-RD=AIA33?D!v#+Xz9B7YF2RW(uvENrNbZ4S&1QT zaqTw4;|#~-g?9qK-gpX7JE_G*dwc<;QbjBY#4($-USL-Jj8X{8X`G zbkM2nfn&bj>iuu4(?-88;2|n|(sse`Wt-y_MoQ=Y=;o}Nx7BF2M_SXN+avcMk9pEP z`mt_ct=hcfb|cmn@?`(P>vhqWdN^mB2D0FzXzvo7*LyEn%@*B#VHGuDJg&^+mOVJO zZ>$~tUDHbc1b%Dm@+IcwlBEX*%CnAC_;qhpUfdFC)w=hujeYPX*z`4cR@5S#WbGGoqTOZu)JNp7QNh;V)QZTjhVlJuq&^pk$FAcSx87-KxPfQ1-_I6Ly z$t~Eib$yD=cE9zugwCjp+6lumkP z;GdK#IeGtCq#tv~?LvWT$EM;-k7h)taS^9`4NC;#yvqwTZX z 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 voGeT%9L+4;fcHHbJ3E`W7&$rH#R5}AaY + + + + + + + + + + From c82bfb8acb9cc056b33583a3fa79efe4a9982214 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 29 Aug 2023 11:55:35 +0200 Subject: [PATCH 136/164] tabled candidate list --- SquirrelInputController.m | 14 +- SquirrelPanel.h | 11 +- SquirrelPanel.m | 695 +++++++++++++++++++++----------------- data/squirrel.yaml | 2 +- 4 files changed, 407 insertions(+), 315 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index b92eb05e0..02ea2da1a 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -23,7 +23,7 @@ @implementation SquirrelInputController { NSString *_composedString; NSRange _selRange; NSUInteger _caretPos; - NSArray *_candidates; + NSArray *_candidates; NSUInteger _lastModifier; int _lastEventCount; RimeSessionId _session; @@ -486,9 +486,9 @@ - (void)showPreeditString:(NSString *)preedit - (void)showPanelWithPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos - candidates:(NSArray *)candidates - comments:(NSArray *)comments - labels:(NSArray *)labels + candidates:(NSArray *)candidates + comments:(NSArray *)comments + labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -577,7 +577,7 @@ - (BOOL)rimeConsumeCommittedText if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); if (_preeditString.length == 0 && _panellessCommitFix) { - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; } [self commitString:commitText]; rime_get_api()->free_commit(&commit); @@ -665,9 +665,9 @@ - (void)rimeUpdate [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; } else { // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. - // note this is a full-shape space U+3000; using half shape characters like "..." will result in + // note this is a full-width EM space U+2003; using narrow characters like "..." will result in // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") + [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index f50268390..6921cb2f1 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -7,6 +7,8 @@ // Linear candidate list, as opposed to stacked candidate list. @property(nonatomic, readonly) BOOL linear; +// Tabled candidate list, a subtype of linear candidate list with tabled layout. +@property(nonatomic, readonly) BOOL tabled; // Vertical text, as opposed to horizontal text. @property(nonatomic, readonly) BOOL vertical; // Show preedit text inline. @@ -21,9 +23,9 @@ - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos - candidates:(NSArray *)candidates - comments:(NSArray *)comments - labels:(NSArray *)labels + candidates:(NSArray *)candidates + comments:(NSArray *)comments + labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -32,7 +34,8 @@ - (void)hide; -- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort; +- (void)updateStatusLong:(NSString *)messageLong + statusShort:(NSString *)messageShort; - (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index a028616da..d062da47a 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -74,6 +74,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat hilitedCornerRadius; +@property(nonatomic, readonly) CGFloat separatorWidth; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @@ -82,6 +83,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) CGFloat lineLength; @property(nonatomic, readonly) BOOL showPaging; @property(nonatomic, readonly) BOOL rememberSize; +@property(nonatomic, readonly) BOOL tabled; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @@ -124,6 +126,7 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius + separatorWidth:(CGFloat)separatorWidth edgeInset:(NSSize)edgeInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace @@ -132,6 +135,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius lineLength:(CGFloat)lineLength showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize + tabled:(BOOL)tabled linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -208,6 +212,7 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor - (void)setCornerRadius:(CGFloat)cornerRadius hilitedCornerRadius:(CGFloat)hilitedCornerRadius + separatorWidth:(CGFloat)separatorWidth edgeInset:(NSSize)edgeInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace @@ -216,12 +221,14 @@ - (void)setCornerRadius:(CGFloat)cornerRadius lineLength:(CGFloat)lineLength showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize + tabled:(BOOL)tabled linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; _hilitedCornerRadius = hilitedCornerRadius; + _separatorWidth = separatorWidth; _edgeInset = edgeInset; _linespace = linespace; _preeditLinespace = preeditLinespace; @@ -230,6 +237,7 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _lineLength = lineLength; _showPaging = showPaging; _rememberSize = rememberSize; + _tabled = tabled; _linear = linear; _vertical = vertical; _inlinePreedit = inlinePreedit; @@ -275,21 +283,28 @@ - (void) setAttrs:(NSMutableDictionary *)attrs fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:[labelAttrs[NSFontAttributeName] pointSize]]; symbolAttrs[NSFontAttributeName] = symbolFont; + if (_vertical || !_linear) { + symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] - symbolFont.leading); + } NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; - symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀\uFE0E"]; + symbolAttrsBackFill[NSGlyphInfoAttributeName] = + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀\uFE0E"]; _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀\uFE0E" attributes:symbolAttrsBackFill]; NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; - symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁\uFE0E"]; + symbolAttrsBackStroke[NSGlyphInfoAttributeName] = + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁\uFE0E"]; _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁\uFE0E" attributes:symbolAttrsBackStroke]; NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; - symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶\uFE0E"]; + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶\uFE0E"]; _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶\uFE0E" attributes:symbolAttrsForwardFill]; NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; - symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷\uFE0E"]; + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷\uFE0E"]; _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷\uFE0E" attributes:symbolAttrsForwardStroke]; } } @@ -322,18 +337,17 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) BOOL isDark; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; -@property(nonatomic, assign) CGFloat separatorWidth; @property(nonatomic, readonly) CAShapeLayer *shape; @property(nonatomic, getter = isFlipped, readonly) BOOL flipped; @property(nonatomic, readonly) BOOL wantsUpdateLayer; -- (void) drawViewWithInsets:(NSEdgeInsets)insets - candidateRanges:(NSArray *)candidateRanges - highlightedIndex:(NSUInteger)highlightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton; +- (void)drawViewWithInsets:(NSEdgeInsets)insets + candidateRanges:(NSArray *)candidateRanges + highlightedIndex:(NSUInteger)highlightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange + pagingButton:(NSUInteger)pagingButton; - (NSRect)contentRectForRange:(NSRange)range; @end @@ -387,6 +401,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { textLayoutManager.usesFontLeading = NO; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + textContainer.lineFragmentPadding = 0; textLayoutManager.textContainer = textContainer; NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; [contentStorage addTextLayoutManager:textLayoutManager]; @@ -400,6 +415,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + textContainer.lineFragmentPadding = 0; [layoutManager addTextContainer:textContainer]; _textStorage = [[NSTextStorage alloc] init]; [_textStorage addLayoutManager:layoutManager]; @@ -435,11 +451,7 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) - (NSRect)contentRect { if (@available(macOS 12.0, *)) { [_textView.textLayoutManager ensureLayoutForRange:_textView.textContentStorage.documentRange]; - NSRect rect = [_textView.textLayoutManager usageBoundsForTextContainer]; - if (@available(macOS 14.0, *)) { // lineFragments no longer include Paddings as of macOS 14 - rect = NSInsetRect(rect, -_textView.textContainer.lineFragmentPadding, 0); - } - return rect; + return [_textView.textLayoutManager usageBoundsForTextContainer]; } else { [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; return [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; @@ -453,13 +465,14 @@ - (NSRect)contentRectForRange:(NSRange)range { __block NSRect contentRect = NSZeroRect; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { contentRect = NSUnionRect(contentRect, segFrame); return YES; }]; - CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:NSMaxRange(range)-1 effectiveRange:NULL] lineSpacing]; + CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName + atIndex:NSMaxRange(range) - 1 effectiveRange:NULL] lineSpacing]; contentRect.size.height += lineSpacing; return contentRect; } else { @@ -473,28 +486,26 @@ - (NSRect)contentRectForRange:(NSRange)range { if (NSMaxRange(glyphRange) <= NSMaxRange(firstLineRange)) { CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x - : NSWidth(firstLineRect) - textContainer.lineFragmentPadding; + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(firstLineRect); return NSMakeRect(NSMinX(firstLineRect) + startX, NSMinY(firstLineRect), endX - startX, NSHeight(firstLineRect)); } else { - NSRect finalLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange)-1 + NSRect finalLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 effectiveRange:NULL]; - return NSMakeRect(textContainer.lineFragmentPadding, NSMinY(firstLineRect), - textContainer.size.width - textContainer.lineFragmentPadding * 2, - NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + return NSMakeRect(NSMinX(firstLineRect), NSMinY(firstLineRect), + textContainer.size.width, NSMaxY(finalLineRect) - NSMinY(firstLineRect)); } } } // Will triger - (void)drawRect:(NSRect)dirtyRect -- (void) drawViewWithInsets:(NSEdgeInsets)insets - candidateRanges:(NSArray *)candidateRanges - highlightedIndex:(NSUInteger)highlightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton { +- (void)drawViewWithInsets:(NSEdgeInsets)insets + candidateRanges:(NSArray *)candidateRanges + highlightedIndex:(NSUInteger)highlightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange + pagingButton:(NSUInteger)pagingButton { _insets = insets; _candidateRanges = candidateRanges; _highlightedIndex = highlightedIndex; @@ -573,9 +584,9 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead NSMutableArray *lineRanges = [[NSMutableArray alloc] initWithCapacity:2]; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { if (!nearEmptyRect(segFrame)) { segFrame.size.height += lineSpacing; [lineRects addObject:[NSValue valueWithRect:segFrame]]; @@ -586,28 +597,27 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead if (lineRects.count == 1) { *bodyRect = lineRects[0].rectValue; } else { - CGFloat rightEdge = _textView.textContainer.size.width - _textView.textContainer.lineFragmentPadding; + CGFloat containerWidth = _textView.textContainer.size.width; NSRect leadingLineRect = lineRects.firstObject.rectValue; - leadingLineRect.size.width = rightEdge - NSMinX(leadingLineRect); + leadingLineRect.size.width = containerWidth - NSMinX(leadingLineRect); NSRect trailingLineRect = lineRects.lastObject.rectValue; - CGFloat leftEdge = _textView.textContainer.lineFragmentPadding; if (NSMaxX(trailingLineRect) == NSMaxX(leadingLineRect)) { if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { *bodyRect = NSUnionRect(leadingLineRect, trailingLineRect); } else { *leadingRect = leadingLineRect; - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { *trailingRect = trailingLineRect; if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { - *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { *leadingRect = leadingLineRect; if (lineRanges.lastObject.location > lineRanges.firstObject.endLocation) { - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } @@ -615,7 +625,6 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead } } else { NSLayoutManager *layoutManager = _textView.layoutManager; - NSTextContainer *textContainer = _textView.textContainer; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); @@ -624,40 +633,37 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x - : NSWidth(leadingLineRect) - textContainer.lineFragmentPadding; + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect); *bodyRect = NSMakeRect(startX, NSMinY(leadingLineRect), endX - startX, NSHeight(leadingLineRect)); } else { - CGFloat rightEdge = textContainer.size.width - textContainer.lineFragmentPadding; + CGFloat containerWidth = _textView.textContainer.size.width; NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 effectiveRange:&trailingLineRange]; CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x - : NSWidth(trailingLineRect) - textContainer.lineFragmentPadding; - CGFloat leftEdge = textContainer.lineFragmentPadding; + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(trailingLineRect); if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); } else { *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), - rightEdge - startX, NSHeight(leadingLineRect)); - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + containerWidth - startX, NSHeight(leadingLineRect)); + *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { - *trailingRect = NSMakeRect(leftEdge, NSMinY(trailingLineRect), - endX - leftEdge, NSHeight(trailingLineRect)); + *trailingRect = NSMakeRect(0.0, NSMinY(trailingLineRect), endX, + NSHeight(trailingLineRect)); if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(leftEdge, NSMinY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), - rightEdge - startX, NSHeight(leadingLineRect)); + containerWidth - startX, NSHeight(leadingLineRect)); if (trailingLineRange.location > NSMaxRange(leadingLineRange)) { - *bodyRect = NSMakeRect(leftEdge, NSMaxY(leadingLineRect), rightEdge - leftEdge, + *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } @@ -729,13 +735,14 @@ - (void)updateLayer { NSBezierPath *highlightedPath; NSBezierPath *highlightedPreeditPath; NSBezierPath *candidateBlockPath; + NSBezierPath *candidateHorzGridPath; + NSBezierPath *candidateVertGridPath; NSBezierPath *pageUpPath; NSBezierPath *pageDownPath; SquirrelTheme *theme = self.currentTheme; NSRect backgroundRect = self.bounds; NSRect textContainerRect = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); - CGFloat linePadding = _textView.textContainer.lineFragmentPadding; NSRange visibleRange; if (@available(macOS 12.0, *)) { @@ -759,9 +766,9 @@ - (void)updateLayer { } } if (candidateBlockRange.length > 0) { - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace/2); + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace / 2); if (preeditRange.length == 0) { - candidateBlockRect.origin.y += theme.linespace/2; + candidateBlockRect.origin.y += theme.linespace / 2; } } if (!theme.linear && pagingRange.length > 0) { @@ -778,7 +785,7 @@ - (void)updateLayer { // Draw highlighted part of preedit text NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect innerBox = NSInsetRect(preeditRect, linePadding, 0); + NSRect innerBox = NSInsetRect(preeditRect, theme.separatorWidth / 2, 0); if (candidateBlockRange.length > 0) { innerBox.size.height -= theme.preeditLinespace; } @@ -787,11 +794,11 @@ - (void)updateLayer { NSRect trailingRect = NSZeroRect; [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), innerBox); bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), innerBox); trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), innerBox); + : NSIntersectionRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), innerBox); NSArray *highlightedPreeditPoints; NSArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated @@ -802,9 +809,9 @@ - (void)updateLayer { } else { highlightedPreeditPoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } - highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight/3)); + highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); if (highlightedPreeditPoints2.count > 0) { - [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight/3))]; + [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; } } } @@ -818,86 +825,122 @@ - (void)updateLayer { candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.hilitedCornerRadius); // Draw candidate highlight rect + if (theme.linear) { + CGFloat gridOriginY = NSMinY(candidateBlockRect); + CGFloat tabInterval = theme.separatorWidth * 2; + if (theme.tabled) { + candidateHorzGridPath = [NSBezierPath bezierPath]; + candidateVertGridPath = [NSBezierPath bezierPath]; + } for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); if (candidateRange.length == 0) { break; } - if (theme.linear) { - NSRect leadingRect = NSZeroRect; - NSRect bodyRect = NSZeroRect; - NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(leadingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(bodyRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(trailingRect, textContainerRect.origin.x, textContainerRect.origin.y), -MIN(_separatorWidth / 2, linePadding), 0); - if (preeditRange.length == 0) { - leadingRect.origin.y += theme.linespace / 2; - bodyRect.origin.y += theme.linespace / 2; - trailingRect.origin.y += theme.linespace / 2; + NSRect leadingRect = NSZeroRect; + NSRect bodyRect = NSZeroRect; + NSRect trailingRect = NSZeroRect; + [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + if (preeditRange.length == 0) { + leadingRect.origin.y += theme.linespace / 2; + bodyRect.origin.y += theme.linespace / 2; + trailingRect.origin.y += theme.linespace / 2; + } + if (!NSIsEmptyRect(leadingRect)) { + leadingRect.origin.y -= theme.linespace / 2; + leadingRect.size.height += theme.linespace / 2; + leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); + } + if (!NSIsEmptyRect(trailingRect)) { + trailingRect.size.height += theme.linespace / 2; + trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); + } + if (!NSIsEmptyRect(bodyRect)) { + if (NSIsEmptyRect(leadingRect)) { + bodyRect.origin.y -= theme.linespace / 2; + bodyRect.size.height += theme.linespace / 2; } - if (!NSIsEmptyRect(leadingRect)) { - leadingRect.origin.y -= theme.linespace / 2; - leadingRect.size.height += theme.linespace / 2; - leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); + if (NSIsEmptyRect(trailingRect)) { + bodyRect.size.height += theme.linespace / 2; } - if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.height += theme.linespace / 2; - trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); + bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); + } + if (theme.tabled) { + CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border + [candidateHorzGridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth / 2, bottomEdge)]; + [candidateHorzGridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth / 2, bottomEdge)]; + [candidateHorzGridPath closePath]; + gridOriginY = bottomEdge; } - if (!NSIsEmptyRect(bodyRect)) { - if (NSIsEmptyRect(leadingRect)) { - bodyRect.origin.y -= theme.linespace / 2; - bodyRect.size.height += theme.linespace / 2; - } - if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.height += theme.linespace / 2; - } - bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); + CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; + if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar + [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.hilitedCornerRadius)]; + [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.hilitedCornerRadius)]; + [candidateVertGridPath closePath]; } - NSArray *candidatePoints; - NSArray *candidatePoints2; - // Handles the special case where containing boxes are separated - if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && - !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePoints = rectVertex(leadingRect); - candidatePoints2 = rectVertex(trailingRect); + CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + CGFloat tabPosition = ceil((tailEdge + theme.separatorWidth / 2 - _insets.left) / tabInterval) * tabInterval + _insets.left; + if (NSIsEmptyRect(trailingRect)) { + bodyRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + } else if (NSIsEmptyRect(bodyRect)) { + trailingRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; } else { - candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); + bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); + trailingRect = NSZeroRect; } - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); - if (candidatePoints2.count > 0) { - [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; - } - _candidatePaths[i] = candidatePath; + } + NSArray *candidatePoints; + NSArray *candidatePoints2; + // Handles the special case where containing boxes are separated + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && + !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + candidatePoints = rectVertex(leadingRect); + candidatePoints2 = rectVertex(trailingRect); } else { - NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); - candidateRect.size.width = textContainerRect.size.width; - candidateRect.origin.x = textContainerRect.origin.x; - candidateRect.origin.y += textContainerRect.origin.y; - if (preeditRange.length == 0) { - candidateRect.origin.y += theme.linespace / 2; - } - candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); - NSArray *candidatePoints = rectVertex(candidateRect); - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); - _candidatePaths[i] = candidatePath; + candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); + } + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + if (candidatePoints2.count > 0) { + [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; + } + _candidatePaths[i] = candidatePath; + } + } else { // stacked layout + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { + NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + if (candidateRange.length == 0) { + break; } + NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); + candidateRect.size.width = textContainerRect.size.width; + candidateRect.origin.x = textContainerRect.origin.x; + candidateRect.origin.y += textContainerRect.origin.y; + if (preeditRange.length == 0) { + candidateRect.origin.y += theme.linespace / 2; + } + candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); + NSArray *candidatePoints = rectVertex(candidateRect); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + _candidatePaths[i] = candidatePath; } } +} // Draw paging Rect if (pagingRange.length > 0) { - CGFloat buttonPadding = theme.linear ? MIN(_separatorWidth / 2, linePadding) : linePadding; NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 2, 2)], - textContainerRect.origin.x, textContainerRect.origin.y); - pageDownRect.size.width += buttonPadding; + _insets.left, theme.edgeInset.height); + pageDownRect.size.width += theme.separatorWidth / 2; NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 2)], - textContainerRect.origin.x, textContainerRect.origin.y); - pageUpRect.origin.x -= buttonPadding; + _insets.left, theme.edgeInset.height); + pageUpRect.origin.x -= theme.separatorWidth / 2; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented if (theme.linear) { pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace / 2); @@ -910,18 +953,18 @@ - (void)updateLayer { pageDownRect = NSIntersectionRect(pageDownRect, textContainerRect); pageUpRect = NSIntersectionRect(pageUpRect, textContainerRect); pageDownPath = drawRoundedPolygon(rectVertex(pageDownRect), - MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect))/3)); + MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3)); pageUpPath = drawRoundedPolygon(rectVertex(pageUpRect), - MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect))/3)); + MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect)) / 3)); _pagingPaths[0] = pageUpPath; _pagingPaths[1] = pageDownPath; } // Draw borders backgroundPath = drawRoundedPolygon(rectVertex(backgroundRect), - MIN(theme.cornerRadius, NSHeight(backgroundRect)/3)); + MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3)); textContainerPath = drawRoundedPolygon(rectVertex(textContainerRect), - MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3)); + MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect) / 3)); if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { borderPath = [backgroundPath copy]; [borderPath appendBezierPath:textContainerPath]; @@ -931,33 +974,33 @@ - (void)updateLayer { // set layers _shape.path = [backgroundPath quartzPath]; _shape.fillColor = [[NSColor whiteColor] CGColor]; - _shape.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + _shape.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; textContainerLayer.path = [textContainerPath quartzPath]; textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; - textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect)/3); + textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect) / 3); [self.layer setSublayers:nil]; - self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); CALayer *panelLayer = [[CALayer alloc] init]; [self.layer addSublayer:panelLayer]; if (theme.backgroundImage) { CAShapeLayer *backgroundImageLayer = [[CAShapeLayer alloc] init]; if (theme.vertical) { - const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI/2); - backgroundImageLayer.path = CGPathCreateCopyByTransformingPath([textContainerPath quartzPath], &rotate); + const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI / 2); + backgroundImageLayer.path = CFAutorelease(CGPathCreateCopyByTransformingPath([textContainerPath quartzPath], &rotate)); backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; - [backgroundImageLayer setAffineTransform:CGAffineTransformMakeRotation(M_PI/2)]; + [backgroundImageLayer setAffineTransform:CGAffineTransformMakeRotation(M_PI / 2)]; } else { backgroundImageLayer.path = [textContainerPath quartzPath]; backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; } - backgroundImageLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + backgroundImageLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); [panelLayer addSublayer:backgroundImageLayer]; } CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; backgroundLayer.path = [textContainerPath quartzPath]; backgroundLayer.fillColor = [theme.backgroundColor CGColor]; - backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect)/3); + backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); [panelLayer addSublayer:backgroundLayer]; if (theme.preeditBackgroundColor && (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { @@ -1022,6 +1065,20 @@ - (void)updateLayer { [self.layer addSublayer:highlightedPreeditLayer]; } } + if (theme.tabled) { + CAShapeLayer *horzGridLayer = [[CAShapeLayer alloc] init]; + horzGridLayer.path = [candidateHorzGridPath quartzPath]; + horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + horzGridLayer.lineWidth = 0.5; + horzGridLayer.lineCap = kCALineCapRound; + [panelLayer addSublayer:horzGridLayer]; + CAShapeLayer *vertGridLayer = [[CAShapeLayer alloc] init]; + vertGridLayer.path = [candidateVertGridPath quartzPath]; + vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + vertGridLayer.lineWidth = 1; + vertGridLayer.lineCap = kCALineCapRound; + [panelLayer addSublayer:vertGridLayer]; + } if (theme.borderColor && !borderPath.empty) { CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; borderLayer.path = [borderPath quartzPath]; @@ -1029,6 +1086,7 @@ - (void)updateLayer { borderLayer.fillRule = kCAFillRuleEvenOdd; [panelLayer addSublayer:borderLayer]; } + [_textView display]; } - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { @@ -1066,9 +1124,9 @@ @implementation SquirrelPanel { NSString *_preedit; NSRange _selRange; NSUInteger _caretPos; - NSArray *_candidates; - NSArray *_comments; - NSArray *_labels; + NSArray *_candidates; + NSArray *_comments; + NSArray *_labels; NSUInteger _index; NSUInteger _pageNum; NSUInteger _turnPage; @@ -1088,6 +1146,10 @@ - (BOOL)linear { return _view.currentTheme.linear; } +- (BOOL)tabled { + return _view.currentTheme.tabled; +} + - (BOOL)vertical { return _view.currentTheme.vertical; } @@ -1128,7 +1190,6 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; - defaultAttrs[NSStrokeWidthAttributeName] = @(-1.0); NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; @@ -1214,7 +1275,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless + styleMask:NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; @@ -1302,10 +1363,14 @@ - (void)sendEvent:(NSEvent *)event { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { [_inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex + pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos candidates:_candidates comments:_comments labels:_labels highlighted:_index pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments labels:_labels highlighted:_index + pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; } } } break; @@ -1349,34 +1414,34 @@ - (void)sendEvent:(NSEvent *)event { } - (void)getCurrentScreen { - // get current screen _screen = [NSScreen mainScreen]; - NSArray *screens = [NSScreen screens]; + NSArray *screens = [NSScreen screens]; for (NSUInteger i = 0; i < screens.count; ++i) { if (NSPointInRect(_position.origin, [screens[i] frame])) { _screen = screens[i]; - break; + return; } } } - (void)getTextWidthLimit { - SquirrelTheme *theme = _view.currentTheme; [self getCurrentScreen]; NSRect screenRect = _screen.visibleFrame; + SquirrelTheme *theme = _view.currentTheme; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _textWidthLimit = floor((theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - (theme.hilitedCornerRadius + theme.edgeInset.width) * 2); + _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - theme.separatorWidth - theme.edgeInset.width * 2; if (theme.lineLength > 0) { - _textWidthLimit = MIN(floor(theme.lineLength), _textWidthLimit); + _textWidthLimit = MIN(theme.lineLength, _textWidthLimit); + } + if (theme.tabled) { + CGFloat tabInterval = theme.separatorWidth * 2; + _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; } - CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - theme.edgeInset.height * 2; - _view.textView.textContainer.size = NSMakeSize(_textWidthLimit + theme.hilitedCornerRadius * 2, textHeightLimit); + _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX); } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { - SquirrelTheme *theme = _view.currentTheme; - if (@available(macOS 10.14, *)) { NSAppearance *requestedAppearance = [NSAppearance appearanceNamed:(_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; if (self.appearance != requestedAppearance) { @@ -1385,8 +1450,8 @@ - (void)show { } //Break line if the text is too long, based on screen size. + SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; - CGFloat linePadding = textContainer.lineFragmentPadding; NSEdgeInsets insets = _view.insets; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = _screen.visibleFrame; @@ -1395,7 +1460,7 @@ - (void)show { // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; - NSRect maxContentRect = NSInsetRect(contentRect, linePadding, 0); + NSRect maxContentRect = contentRect; if (theme.lineLength > 0) { // fixed line length / text width if (_maxSize.width > 0) { // not applicable to status message where maxSize is set to 0 maxContentRect.size.width = _textWidthLimit; @@ -1404,13 +1469,13 @@ - (void)show { if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) - : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + linePadding + insets.right > NSMaxX(screenRect)))) && + : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect)))) && theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - [textContainer setSize:NSMakeSize(_maxSize.width + linePadding * 2, textHeightLimit)]; + [textContainer setSize:NSMakeSize(_maxSize.width, textHeightLimit)]; } } if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) @@ -1419,7 +1484,7 @@ - (void)show { _maxSize.height = NSHeight(maxContentRect); } else { maxContentRect.size.height = _maxSize.height; - [textContainer setSize:NSMakeSize(_textWidthLimit + linePadding * 2, _maxSize.height)]; + [textContainer setSize:NSMakeSize(_textWidthLimit, _maxSize.height)]; } } } @@ -1427,10 +1492,10 @@ - (void)show { NSRect windowRect; if (theme.vertical) { windowRect.size = NSMakeSize(NSHeight(maxContentRect) + insets.top + insets.bottom, - NSWidth(maxContentRect) + linePadding * 2 + insets.left + insets.right); + NSWidth(maxContentRect) + insets.left + insets.right); // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left + linePadding : -kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -kOffsetHeight) - NSHeight(windowRect); } else { windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); } @@ -1441,7 +1506,7 @@ - (void)show { windowRect.origin.x += NSHeight(preeditRect) + insets.top; } } else { - windowRect.size = NSMakeSize(NSWidth(maxContentRect) + linePadding * 2 + insets.left + insets.right, + windowRect.size = NSMakeSize(NSWidth(maxContentRect) + insets.left + insets.right, NSHeight(maxContentRect) + insets.top + insets.bottom); if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa @@ -1452,7 +1517,7 @@ - (void)show { } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left - linePadding, + windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } } @@ -1480,11 +1545,11 @@ - (void)show { // rotate the view, the core in vertical mode! if (theme.vertical) { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward|NSAlignMaxYInward|NSAlignWidthNearest|NSAlignHeightNearest] display:YES]; + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:YES]; [self.contentView setBoundsRotation:-90.0]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward|NSAlignMaxYInward|NSAlignWidthNearest|NSAlignHeightNearest] display:YES]; + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:YES]; [self.contentView setBoundsRotation:0.0]; [self.contentView setBoundsOrigin:NSZeroPoint]; } @@ -1531,11 +1596,17 @@ - (void)setLayoutForRange:(NSRange)charRange style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; if (@available(macOS 12.0, *)) { + if (verticalLayout) { + [_view.textStorage addAttribute:CFBridgingRelease(kCTBaselineClassAttributeName) + value:CFBridgingRelease(kCTBaselineClassIdeographicCentered) range:charRange]; + } NSUInteger i = charRange.location; NSRange runRange = NSMakeRange(i, 0); while (i < NSMaxRange(charRange)) { - CGFloat baselineOffset = [[_view.textStorage attribute:NSBaselineOffsetAttributeName atIndex:i effectiveRange:&runRange] doubleValue]; - [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset + lineHeight/2 - refFontHeight/2) range:runRange]; + NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName + atIndex:i effectiveRange:&runRange]; + CGFloat offset = (baselineOffset ? baselineOffset.doubleValue : 0.0) + lineHeight / 2 - refFontHeight / 2; + [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:runRange]; i = NSMaxRange(runRange); } } else { @@ -1546,19 +1617,22 @@ - (void)setLayoutForRange:(NSRange)charRange while (i < NSMaxRange(glyphRange)) { NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; - CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight/2 + refFont.descender + refFontHeight/2); + CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight / 2 + refFont.descender + refFontHeight / 2); // typesetting glyphs NSRange fontRunRange = NSMakeRange(NSNotFound, 0); NSUInteger j = lineRange.location; while (j < NSMaxRange(lineRange)) { NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; - NSFont *runFont = [_view.textStorage attribute:NSFontAttributeName atIndex:runCharLocation effectiveRange:&fontRunRange]; + NSFont *runFont = [_view.textStorage attribute:NSFontAttributeName + atIndex:runCharLocation effectiveRange:&fontRunRange]; NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; - NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName atIndex:runCharLocation effectiveRange:NULL]; + NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName + atIndex:runCharLocation effectiveRange:NULL]; CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; NSRange fontRunGlyphRange = [layoutManager glyphRangeForCharacterRange:fontRunRange actualCharacterRange:NULL]; - NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange)-j), [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange) - j), + [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); if (verticalLayout) { NSNumber *verticalGlyph = [_view.textStorage attribute:NSVerticalGlyphFormAttributeName atIndex:runCharLocation effectiveRange:NULL]; if (verticalGlyph ? verticalGlyph.boolValue : YES) { @@ -1570,9 +1644,9 @@ - (void)setLayoutForRange:(NSRange)charRange CGFloat runFontHeight = runFont.ascender - runFont.descender; CGFloat systemFontHeight = systemFont.ascender - systemFont.descender; if (verticalLayout) { - runGlyphPosition.y = alignment - offset + runBaseline - runFontHeight/2 + round(MAX(0.0, runFontHeight - systemFontHeight) / 3); + runGlyphPosition.y = alignment - offset + runBaseline - runFontHeight / 2 + round(MAX(0.0, runFontHeight - systemFontHeight) / 3); if (runFont.vertical) { - runGlyphPosition.x += round(MAX(0.0, runFontHeight - systemFontHeight) * 2/3); + runGlyphPosition.x += round(MAX(0.0, runFontHeight - systemFontHeight) * 2 / 3); } } else { runGlyphPosition.y = alignment - offset; @@ -1586,14 +1660,15 @@ - (void)setLayoutForRange:(NSRange)charRange } - (BOOL)shouldBreakLineWithRange:(NSRange)range { + [_view.textStorage fixFontAttributeInRange:range]; if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; NSUInteger __block lineCount = 0; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { ++lineCount; return YES; }]; @@ -1605,8 +1680,8 @@ - (BOOL)shouldBreakLineWithRange:(NSRange)range { NSRange lineRange = NSMakeRange(loc, 0); NSUInteger lineCount = 0; while (loc < NSMaxRange(glyphRange)) { - [_view.textView.layoutManager lineFragmentRectForGlyphAtIndex:loc - effectiveRange:&lineRange]; + [_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:loc + effectiveRange:&lineRange]; ++lineCount; loc = NSMaxRange(lineRange); } @@ -1614,33 +1689,28 @@ - (BOOL)shouldBreakLineWithRange:(NSRange)range { } } -- (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLength{ +- (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLength { + [_view.textStorage fixFontAttributeInRange:range]; if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromRange:range]; CGFloat __block rangeEdge; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { rangeEdge = NSMaxX(segFrame); return YES; }]; - NSRect container; - if (@available(macOS 14.0, *)) { - container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; - } else { - container = NSInsetRect([_view.textView.textLayoutManager usageBoundsForTextContainer], - _view.textView.textContainer.lineFragmentPadding, 0); - } - *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); + [_view.textView.textLayoutManager ensureLayoutForRange:_view.textView.textContentStorage.documentRange]; + NSRect container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; + *maxLineLength = MAX(MIN(_textWidthLimit, NSWidth(container)), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } else { NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; CGFloat rangeEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); - NSRect container = NSInsetRect([_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer], - _view.textView.textContainer.lineFragmentPadding, 0); - *maxLineLength = MAX(MIN(_textWidthLimit, ceil(NSWidth(container))), _maxSize.width); + NSRect container = [_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer]; + *maxLineLength = MAX(MIN(_textWidthLimit, NSWidth(container)), _maxSize.width); return NSMinX(container) + *maxLineLength > rangeEdge; } } @@ -1649,9 +1719,9 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos - candidates:(NSArray *)candidates - comments:(NSArray *)comments - labels:(NSArray *)labels + candidates:(NSArray *)candidates + comments:(NSArray *)comments + labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -1672,13 +1742,13 @@ - (void)showPreedit:(NSString *)preedit if (numCandidates == 0) { _index = index = NSNotFound; } - _turnPage = turnPage; if (_turnPage == NSPageUpFunctionKey) { turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; } else if (_turnPage == NSPageDownFunctionKey) { turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; } + [self getTextWidthLimit]; if (numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; @@ -1697,19 +1767,24 @@ - (void)showPreedit:(NSString *)preedit } SquirrelTheme *theme = _view.currentTheme; - _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; - _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; - [self getTextWidthLimit]; + _view.textView.layoutOrientation = theme.vertical ? + NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; if (theme.lineLength > 0) { - _maxSize.width = MIN(floor(theme.lineLength), _textWidthLimit); + _maxSize.width = MIN(theme.lineLength, _textWidthLimit); } - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width, - theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width); + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, + theme.edgeInset.width + theme.separatorWidth / 2, + theme.edgeInset.height + theme.linespace / 2, + theme.edgeInset.width + theme.separatorWidth / 2); NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); + NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; + NSRange pagingRange = NSMakeRange(NSNotFound, 0); + NSMutableParagraphStyle *paragraphStyleCandidate; + // preedit if (preedit) { NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; @@ -1739,23 +1814,22 @@ - (void)showPreedit:(NSString *)preedit } preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; + + insets.top = theme.edgeInset.height; if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { insets.bottom = theme.edgeInset.height; + goto typesetter; } - insets.top = theme.edgeInset.height; } - // separator - NSMutableAttributedString *sep = [[NSMutableAttributedString alloc] initWithString:@" " attributes:theme.attrs]; - _view.separatorWidth = theme.linear ? sep.size.width : 0.0; - // candidate items NSUInteger candidateBlockStart = text.length; - NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; NSUInteger lineStart = text.length; - NSRange separatorRange = NSMakeRange(NSNotFound, 0); + if (theme.linear) { + paragraphStyleCandidate = [theme.paragraphStyle copy]; + } for (NSUInteger i = 0; i < candidates.count; ++i) { NSMutableAttributedString *item = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; @@ -1767,18 +1841,15 @@ - (void)showPreedit:(NSString *)preedit NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; NSString *prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:prefixLabelString - attributes:labelAttrs]]; + initWithString:prefixLabelString attributes:labelAttrs]]; if (!theme.linear) { // get the label size for indent labelWidth = item.size.width; } } NSUInteger candidateStart = item.length; - NSString *candidate = candidates[i]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:candidate - attributes:attrs]]; + initWithString:candidates[i] attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. [item addAttribute:NSWritingDirectionAttributeName value:@[@0] @@ -1787,42 +1858,35 @@ - (void)showPreedit:(NSString *)preedit if (i < comments.count && [comments[i] length] != 0) { NSString *comment = [@" " stringByAppendingString:comments[i]]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:comment - attributes:commentAttrs]]; + initWithString:comment attributes:commentAttrs]]; } if (theme.suffixLabelFormat != nil) { NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; NSString *suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:suffixLabelString - attributes:labelAttrs]]; + initWithString:suffixLabelString attributes:labelAttrs]]; } - NSMutableParagraphStyle *paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; if (!theme.linear) { + paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.headIndent = labelWidth; } [item addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate range:NSMakeRange(0, item.length)]; // determine if the line is too wide and line break is needed, based on screen size. - NSString *separatorString = theme.linear ? @" " : @"\n"; if (i > 0) { NSUInteger separatorStart = text.length; - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] - initWithString:separatorString - attributes:theme.attrs]; - [separator addAttribute:NSVerticalGlyphFormAttributeName - value:@NO + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? @" \t" : @" " ) : @"\n" attributes:theme.attrs]; + [separator addAttribute:NSVerticalGlyphFormAttributeName value:@NO range:NSMakeRange(0, separator.length)]; - separatorRange = NSMakeRange(separatorStart, separator.length); + NSRange separatorRange = NSMakeRange(separatorStart, separator.length); [text appendAttributedString:separator]; [text appendAttributedString:item]; if (theme.linear && [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { - [text replaceCharactersInRange:separatorRange withString:@"\n"]; - lineStart = separatorStart + 1; - separatorRange = NSMakeRange(NSNotFound, 0); + [text replaceCharactersInRange:separatorRange withString:theme.tabled ? @" \n" : @"\n"]; + lineStart = separatorStart + (theme.tabled ? 2 : 1); } } else { [text appendAttributedString:item]; @@ -1831,67 +1895,67 @@ - (void)showPreedit:(NSString *)preedit } // paging indication - NSRange pagingRange = NSMakeRange(NSNotFound, 0); - if (numCandidates > 0 && theme.showPaging) { - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] init]; - - NSMutableAttributedString *pageUpString = [[NSMutableAttributedString alloc] initWithAttributedString:(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke)]; + if (theme.showPaging) { + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] + initWithAttributedString:(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke)]; + [paging appendAttributedString:[[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; + [paging appendAttributedString:[[NSAttributedString alloc] + initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]]; if (_turnPage == NSPageUpFunctionKey) { - [pageUpString addAttributes:theme.pagingHighlightedAttrs - range:NSMakeRange(0, pageUpString.length)]; - } - [paging appendAttributedString:pageUpString]; - - [paging appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; - - NSMutableAttributedString *pageDownString = [[NSMutableAttributedString alloc] initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]; - if (_turnPage == NSPageDownFunctionKey) { - [pageDownString addAttributes:theme.pagingHighlightedAttrs - range:NSMakeRange(0, pageDownString.length)]; + [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(0, 2)]; + } else if (_turnPage == NSPageDownFunctionKey) { + [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 2, 2)]; } - [paging appendAttributedString:pageDownString]; - [text appendAttributedString:[[NSAttributedString alloc] - initWithString:theme.linear ? @" " : @"\n" - attributes:theme.attrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? @" \t" : @" ") : @"\n" attributes:theme.attrs]]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; + [text appendAttributedString:paging]; if (theme.linear) { - [text appendAttributedString:paging]; if ([self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { - if (separatorRange.length > 0) { - [text replaceCharactersInRange:separatorRange withString:@"\n"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:theme.tabled ? @"\n\t" : @"\n "]; + lineStart = pagingStart; + pagingStart += 1; + } + if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { + paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + CGFloat tabInterval = theme.separatorWidth * 2; + if (theme.tabled) { + maxLineLength = ceil((maxLineLength) / tabInterval) * tabInterval - theme.separatorWidth; } else { - [text insertAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.attrs] atIndex:pagingStart-1]; - pagingStart += 1; + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; } - } - NSMutableParagraphStyle *paragraphStylePaging = [theme.paragraphStyle mutableCopy]; - if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, text.length - pagingStart) maxLineLength:&maxLineLength]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart-1, 1) withString:@"\t"]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; + CGFloat candidateEndPosition = [text attributedSubstringFromRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)].size.width; + NSMutableArray *tabStops = [[NSMutableArray alloc] init]; + for (NSUInteger i = 1; tabInterval * i < candidateEndPosition; ++i) { + [tabStops addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:tabInterval * i]]; + } + [tabStops addObject:[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; + paragraphStyleCandidate.tabStops = tabStops; } [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePaging - range:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart)]; + value:paragraphStyleCandidate + range:NSMakeRange(lineStart, text.length - lineStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { - [paging replaceCharactersInRange:NSMakeRange(pageUpString.length, 1) withString:@"\t"]; - [paging replaceCharactersInRange:NSMakeRange(paging.length - pageDownString.length - 1, 1) withString:@"\t"]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength/2], + [text replaceCharactersInRange:NSMakeRange(pagingStart + 2, 1) withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 3, 1) withString:@"\t"]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength / 2], [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; } - [paging addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePaging - range:NSMakeRange(0, paging.length)]; - [text appendAttributedString:paging]; + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePaging + range:NSMakeRange(pagingStart, paging.length)]; insets.bottom = theme.edgeInset.height; } pagingRange = NSMakeRange(text.length - paging.length, paging.length); } - [text fixAttributesInRange:NSMakeRange(0, text.length)]; +typesetter: + [text addAttribute:NSStrokeWidthAttributeName value:@(1 - self.backingScaleFactor) range:NSMakeRange(0, text.length)]; + [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; if (preedit) { [self setLayoutForRange:preeditRange withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) @@ -1910,7 +1974,8 @@ - (void)showPreedit:(NSString *)preedit } // text done! - [self setAnimationBehavior:caretPos == NSNotFound ? NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; + [self setAnimationBehavior:caretPos == NSNotFound ? + NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; [_view drawViewWithInsets:insets candidateRanges:candidateRanges highlightedIndex:index @@ -1941,21 +2006,22 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message } - (void)showStatus:(NSString *)message { - [self getTextWidthLimit]; SquirrelTheme *theme = _view.currentTheme; _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width, - theme.edgeInset.height, theme.edgeInset.width); - _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; - _view.textView.textContainer.lineFragmentPadding = theme.hilitedCornerRadius; + CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:@" " attributes:theme.statusAttrs].size.width; + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2, + theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2); + _view.textView.layoutOrientation = theme.vertical ? + NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSTextStorage *text = _view.textStorage; - [text setAttributedString:[[NSMutableAttributedString alloc] init]]; - [text appendAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; + [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; + [text addAttribute:NSStrokeWidthAttributeName value:@(1 - self.backingScaleFactor) range:NSMakeRange(0, text.length)]; [text fixAttributesInRange:NSMakeRange(0, text.length)]; [self setLayoutForRange:NSMakeRange(0, text.length) - withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) + withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : + theme.statusAttrs[NSFontAttributeName]) paragraphStyle:theme.statusParagraphStyle]; if (_statusTimer) { @@ -2008,8 +2074,8 @@ - (void)hideStatus:(NSTimer *)timer { return nil; } - NSArray *fontNames = [fullname componentsSeparatedByString:@","]; - NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] initWithCapacity:fontNames.count]; + NSArray *fontNames = [fullname componentsSeparatedByString:@","]; + NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] initWithCapacity:fontNames.count]; for (__strong NSString *fontName in fontNames) { fontName = [fontName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; if ([NSFont fontWithName:fontName size:0.0] != nil) { @@ -2029,8 +2095,9 @@ - (void)hideStatus:(NSTimer *)timer { NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); CFRelease(systemFontRef); NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] - arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + NSArray *fallbackDescriptors = + [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] + arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; NSDictionary *attributes = @{NSFontCascadeListAttribute: fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } @@ -2040,7 +2107,7 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { font = font.verticalFont; } CGFloat lineHeight = font.ascender - font.descender; - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; for (NSFontDescriptor *fallback in fallbackList) { NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; if (vertical) { @@ -2051,17 +2118,24 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return lineHeight; } -static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfig *config, NSString *prefix) { +static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, SquirrelConfig *config, NSString *prefix) { NSString *candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { - *isLinearCandidateList = false; + *isLinearCandidateList = NO; + *isTabledCandidateList = NO; } else if ([candidateListLayout isEqualToString:@"linear"]) { - *isLinearCandidateList = true; + *isLinearCandidateList = YES; + *isTabledCandidateList = NO; + } else if ([candidateListLayout isEqualToString:@"tabled"]) { + // `tabled` is a derived layout of `linear`; tabled implies linear + *isLinearCandidateList = YES; + *isTabledCandidateList = YES; } else { // Deprecated. Not to be confused with text_orientation: horizontal NSNumber *horizontal = [config getOptionalBool:[prefix stringByAppendingString:@"/horizontal"]]; if (horizontal) { *isLinearCandidateList = horizontal.boolValue; + *isTabledCandidateList = NO; } } } @@ -2069,9 +2143,9 @@ static void updateCandidateListLayout(BOOL *isLinearCandidateList, SquirrelConfi static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, NSString *prefix) { NSString *textOrientation = [config getString:[prefix stringByAppendingString:@"/text_orientation"]]; if ([textOrientation isEqualToString:@"horizontal"]) { - *isVerticalText = false; + *isVerticalText = NO; } else if ([textOrientation isEqualToString:@"vertical"]) { - *isVerticalText = true; + *isVerticalText = YES; } else { NSNumber *vertical = [config getOptionalBool:[prefix stringByAppendingString:@"/vertical"]]; if (vertical) { @@ -2087,8 +2161,9 @@ - (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { BOOL linear = NO; + BOOL tabled = NO; BOOL vertical = NO; - updateCandidateListLayout(&linear, config, @"style"); + updateCandidateListLayout(&linear, &tabled, config, @"style"); updateTextOrientation(&vertical, config, @"style"); BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; @@ -2172,7 +2247,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f // the following per-color-scheme configurations, if exist, will // override configurations with the same name under the global 'style' section - updateCandidateListLayout(&linear, config, prefix); + updateCandidateListLayout(&linear, &tabled, config, prefix); updateTextOrientation(&vertical, config, prefix); NSNumber *inlinePreeditOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]]; @@ -2277,23 +2352,34 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) size:fontSize]; - NSFontDescriptor *labelFontDescriptor = getFontDescriptor(labelFontName); + NSDictionary *monoDigitAttrs = @{NSFontFeatureSettingsAttribute: + @[@{NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector)}, + @{NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector)}]}; + NSFontDescriptor *labelFontDescriptor = [(getFontDescriptor(labelFontName) ? : fontDescriptor) + fontDescriptorByAddingAttributes:monoDigitAttrs]; NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] - : (fontDescriptor ? [NSFont fontWithDescriptor:fontDescriptor size:labelFontSize] - : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]); + : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) size:commentFontSize]; - NSFont *pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] - fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:labelFontSize]; + NSFont *pagingFont; + if (@available(macOS 12.0, *)) { + pagingFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + } else { + pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] + fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] + size:labelFontSize]; + } CGFloat fontHeight = getLineHeight(font, vertical); CGFloat labelFontHeight = getLineHeight(labelFont, vertical); CGFloat commentFontHeight = getLineHeight(commentFont, vertical); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); + CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:@" " attributes:@{NSFontAttributeName: font}].size.width; NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; preeditParagraphStyle.minimumLineHeight = fontHeight; @@ -2305,11 +2391,12 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f paragraphStyle.maximumLineHeight = lineHeight; paragraphStyle.paragraphSpacing = lineSpacing / 2; paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + paragraphStyle.tabStops = @[]; + paragraphStyle.defaultTabInterval = separatorWidth * 2; NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; - pagingParagraphStyle.paragraphSpacingBefore = pagingFont.leading; NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; statusParagraphStyle.minimumLineHeight = commentFontHeight; @@ -2338,7 +2425,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - CGFloat labelMidlineOffset = vertical ? 0.0 : font.ascender/2 + font.descender/2 - labelFont.ascender/2 - labelFont.descender/2; + CGFloat labelMidlineOffset = vertical ? 0.0 : floor(font.ascender / 2 + font.descender / 2 - labelFont.ascender / 2 - labelFont.descender / 2); attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelMidlineOffset); @@ -2409,8 +2496,9 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - [theme setCornerRadius:MIN(cornerRadius, lineHeight/2) - hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight/3) + [theme setCornerRadius:MIN(cornerRadius, lineHeight / 2) + hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight / 3) + separatorWidth:separatorWidth edgeInset:edgeInset linespace:lineSpacing preeditLinespace:spacing @@ -2419,6 +2507,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f lineLength:lineLength showPaging:showPaging rememberSize:rememberSize + tabled:tabled linear:linear vertical:vertical inlinePreedit:inlinePreedit diff --git a/data/squirrel.yaml b/data/squirrel.yaml index f11461131..13c8def57 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -25,7 +25,7 @@ style: #horizontal: false # NOTE: do not set a default value for `candidate_list_layout`, in order to # keep the deprecated `horizontal` option working for existing users. - #candidate_list_layout: stacked # stacked | linear + #candidate_list_layout: stacked # stacked | linear | tabled text_orientation: horizontal # horizontal | vertical inline_preedit: true show_paging : true From 179596f79f37f4706fcd19133633f702b60b6a79 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 14 Sep 2023 05:44:55 +0200 Subject: [PATCH 137/164] Update UI --- Sparkle | 2 +- Squirrel.xcodeproj/project.pbxproj | 4 +- SquirrelConfig.m | 2 +- SquirrelInputController.m | 8 +- SquirrelPanel.m | 294 +++++++++++++++++------- data/squirrel.yaml | 2 +- librime | 2 +- macos_keycode.h | 344 ++++++++++++++--------------- macos_keycode.m | 149 ++++++------- 9 files changed, 466 insertions(+), 341 deletions(-) diff --git a/Sparkle b/Sparkle index 5018f4a9e..9684a433e 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 5018f4a9e2dfa289662635b33dbc1a1b3468470f +Subproject commit 9684a433ee0b55da607791c56f501975f5e11ae5 diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index ee6cd7f9b..3c25039f5 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 60; objects = { /* Begin PBXBuildFile section */ @@ -515,7 +515,7 @@ LastUpgradeCheck = 1430; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Squirrel" */; - compatibilityVersion = "Xcode 10.0"; + compatibilityVersion = "Xcode 15.0"; developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 9d804b3f8..f2c685dc0 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -208,7 +208,7 @@ - (NSColor *)patternFromFile:(NSString *)filePath { [fileManager changeCurrentDirectoryPath:[@"~/Library/Rime" stringByStandardizingPath]]; NSString *patternFile = [filePath stringByStandardizingPath]; if ([fileManager fileExistsAtPath:patternFile]) { - NSColor *pattern = [NSColor colorWithPatternImage:[[NSImage alloc] initWithContentsOfFile:patternFile]]; + NSColor *pattern = [NSColor colorWithPatternImage:[[NSImage alloc] initByReferencingFile:patternFile]]; return pattern; } return nil; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 02ea2da1a..dd55f4518 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -577,7 +577,7 @@ - (BOOL)rimeConsumeCommittedText if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); if (_preeditString.length == 0 && _panellessCommitFix) { - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; } [self commitString:commitText]; rime_get_api()->free_commit(&commit); @@ -665,9 +665,7 @@ - (void)rimeUpdate [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; } else { // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. - // note this is a full-width EM space U+2003; using narrow characters like "..." will result in - // an unstable baseline when composing Chinese characters. - [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") + [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") selRange:NSMakeRange(0, 0) caretPos:0]; } } @@ -682,7 +680,7 @@ - (void)rimeUpdate [comments addObject:@""]; } } - NSMutableArray *labels = [NSMutableArray array]; + NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:ctx.menu.page_size]; if (ctx.menu.select_keys) { NSString *selectKeys = [@(ctx.menu.select_keys) stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d062da47a..3a8be2285 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -59,7 +59,8 @@ - (CGPathRef)quartzPath { static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c %@"; +static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kFullWidthSpace = @" "; @interface SquirrelTheme : NSObject @@ -268,20 +269,33 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _statusAttrs = statusAttrs; NSMutableDictionary *symbolAttrs = [pagingAttrs mutableCopy]; if (@available(macOS 12.0, *)) { - symbolAttrs[NSVerticalGlyphFormAttributeName] = @(_vertical && _linear); - - _symbolBackFill = [[NSAttributedString alloc] - initWithString:(_vertical && _linear ? @"􀁧" : @"􀁫") attributes:symbolAttrs]; - _symbolBackStroke = [[NSAttributedString alloc] - initWithString:(_vertical && _linear ? @"􀁦" : @"􀁪") attributes:symbolAttrs]; - _symbolForwardFill = [[NSAttributedString alloc] - initWithString:(_vertical && _linear ? @"􀁩" : @"􀁭") attributes:symbolAttrs]; - _symbolForwardStroke = [[NSAttributedString alloc] - initWithString:(_vertical && _linear ? @"􀁨" : @"􀁬") attributes:symbolAttrs]; + NSTextAttachment *attmBackFill = [[NSTextAttachment alloc] init]; + attmBackFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.backward.circle.fill" accessibilityDescription:nil]; + NSMutableDictionary *attrsBackFill = [symbolAttrs mutableCopy]; + attrsBackFill[NSAttachmentAttributeName] = attmBackFill; + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackFill]; + + NSTextAttachment *attmBackStroke = [[NSTextAttachment alloc] init]; + attmBackStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.backward.circle" accessibilityDescription:nil]; + NSMutableDictionary *attrsBackStroke = [symbolAttrs mutableCopy]; + attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackStroke]; + + NSTextAttachment *attmForwardFill = [[NSTextAttachment alloc] init]; + attmForwardFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.forward.circle.fill" accessibilityDescription:nil]; + NSMutableDictionary *attrsForwardFill = [symbolAttrs mutableCopy]; + attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardFill]; + + NSTextAttachment *attmForwardStroke = [[NSTextAttachment alloc] init]; + attmForwardStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.forward.circle" accessibilityDescription:nil]; + NSMutableDictionary *attrsForwardStroke = [symbolAttrs mutableCopy]; + attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardStroke]; } else { NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] - fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:[labelAttrs[NSFontAttributeName] pointSize]]; + fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] + size:[labelAttrs[NSFontAttributeName] pointSize]]; symbolAttrs[NSFontAttributeName] = symbolFont; if (_vertical || !_linear) { symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] - symbolFont.leading); @@ -289,13 +303,13 @@ - (void) setAttrs:(NSMutableDictionary *)attrs NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; symbolAttrsBackFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀\uFE0E"]; - _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀\uFE0E" attributes:symbolAttrsBackFill]; + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀"]; + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; symbolAttrsBackStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁\uFE0E"]; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁\uFE0E" attributes:symbolAttrsBackStroke]; + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁"]; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; symbolAttrsForwardFill[NSGlyphInfoAttributeName] = @@ -935,10 +949,10 @@ - (void)updateLayer { // Draw paging Rect if (pagingRange.length > 0) { - NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 2, 2)], + NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)], _insets.left, theme.edgeInset.height); pageDownRect.size.width += theme.separatorWidth / 2; - NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 2)], + NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 1)], _insets.left, theme.edgeInset.height); pageUpRect.origin.x -= theme.separatorWidth / 2; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented @@ -1484,7 +1498,6 @@ - (void)show { _maxSize.height = NSHeight(maxContentRect); } else { maxContentRect.size.height = _maxSize.height; - [textContainer setSize:NSMakeSize(_textWidthLimit, _maxSize.height)]; } } } @@ -1563,9 +1576,8 @@ - (void)show { [_view setFrame:frameRect]; [_view.textView setFrame:textFrameRect]; - CGFloat translucency = theme.translucency; if (@available(macOS 10.14, *)) { - if (translucency > 0) { + if (theme.translucency > 0) { [_back setFrame:frameRect]; [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; @@ -1596,17 +1608,20 @@ - (void)setLayoutForRange:(NSRange)charRange style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; if (@available(macOS 12.0, *)) { - if (verticalLayout) { - [_view.textStorage addAttribute:CFBridgingRelease(kCTBaselineClassAttributeName) - value:CFBridgingRelease(kCTBaselineClassIdeographicCentered) range:charRange]; - } NSUInteger i = charRange.location; NSRange runRange = NSMakeRange(i, 0); while (i < NSMaxRange(charRange)) { - NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName - atIndex:i effectiveRange:&runRange]; + NSDictionary *attrs = [_view.textStorage attributesAtIndex:i + longestEffectiveRange:&runRange inRange:charRange]; + NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; CGFloat offset = (baselineOffset ? baselineOffset.doubleValue : 0.0) + lineHeight / 2 - refFontHeight / 2; - [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(offset) range:runRange]; + NSNumber *superscript = attrs[NSSuperscriptAttributeName]; + if (superscript) { + NSFont *runFont = verticalLayout ? [attrs[NSFontAttributeName] verticalFont] : attrs[NSFontAttributeName]; + offset += superscript.integerValue == 1 ? runFont.descender / 3 : runFont.ascender / 3; + } + [_view.textStorage addAttribute:NSBaselineOffsetAttributeName + value:@(offset) range:runRange]; i = NSMaxRange(runRange); } } else { @@ -1617,41 +1632,43 @@ - (void)setLayoutForRange:(NSRange)charRange while (i < NSMaxRange(glyphRange)) { NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; - CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight / 2 + refFont.descender + refFontHeight / 2); + CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight / 2 + refFont.xHeight / 2); // typesetting glyphs - NSRange fontRunRange = NSMakeRange(NSNotFound, 0); NSUInteger j = lineRange.location; while (j < NSMaxRange(lineRange)) { NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; - NSFont *runFont = [_view.textStorage attribute:NSFontAttributeName - atIndex:runCharLocation effectiveRange:&fontRunRange]; + NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; + NSDictionary *attrs = [_view.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; + NSFont *runFont = attrs[NSFontAttributeName]; NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; - NSNumber *baselineOffset = [_view.textStorage attribute:NSBaselineOffsetAttributeName - atIndex:runCharLocation effectiveRange:NULL]; + NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; + NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; - NSRange fontRunGlyphRange = [layoutManager glyphRangeForCharacterRange:fontRunRange actualCharacterRange:NULL]; - NSRange runRange = NSIntersectionRange(NSMakeRange(j, NSMaxRange(fontRunGlyphRange) - j), - [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]); + NSNumber *superscript = attrs[NSSuperscriptAttributeName]; if (verticalLayout) { - NSNumber *verticalGlyph = [_view.textStorage attribute:NSVerticalGlyphFormAttributeName atIndex:runCharLocation effectiveRange:NULL]; + NSNumber *verticalGlyph = attrs[NSVerticalGlyphFormAttributeName]; if (verticalGlyph ? verticalGlyph.boolValue : YES) { runFont = runFont.verticalFont; systemFont = systemFont.verticalFont; } } - CGFloat runBaseline = runFont.ascender; CGFloat runFontHeight = runFont.ascender - runFont.descender; CGFloat systemFontHeight = systemFont.ascender - systemFont.descender; + if (superscript) { + offset += superscript.integerValue == 1 ? refFont.ascender / 3 : refFont.descender / 3; + } if (verticalLayout) { - runGlyphPosition.y = alignment - offset + runBaseline - runFontHeight / 2 + round(MAX(0.0, runFontHeight - systemFontHeight) / 3); - if (runFont.vertical) { - runGlyphPosition.x += round(MAX(0.0, runFontHeight - systemFontHeight) * 2 / 3); + if ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassRoman)] || !runFont.vertical) { + runGlyphPosition.y = alignment - offset + refFont.xHeight / 2; + } else { + runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) / 3 : 0.0); + runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) * 2 / 3 : 0.0; } } else { - runGlyphPosition.y = alignment - offset; + runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - refFont.xHeight / 2 : 0.0); } - [layoutManager setLocation:NSMakePoint(round(runGlyphPosition.x), round(runGlyphPosition.y)) forStartOfGlyphRange:runRange]; + [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } i = NSMaxRange(lineRange); @@ -1715,6 +1732,118 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng } } +static NSArray * formatLabels(NSString *format, NSArray *labels) { + NSString *newFormat; + NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; + NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString:[labels componentsJoinedByString:@""]]; + if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff10, 10)] + isSupersetOfSet:labelCharacters]) { // 01..9 + if ([format containsString:@"%c\u20dd"]) { // ①..⑨⓪ + newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20dd" withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x24ea : [label characterAtIndex:0] - 0xff11 + 0x2460, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } else if ([format containsString:@"(%c)"]) { // ⑴..⑼⑽ + newFormat = [format stringByReplacingOccurrencesOfString:@"(%c)" withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x247d : [label characterAtIndex:0] - 0xff11 + 0x2474, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } else if ([format containsString:@"%c."]) { // ⒈..⒐🄀 + newFormat = [format stringByReplacingOccurrencesOfString:@"%c." withString:@"%S"]; + for (NSString *label in labels) { + if ([label characterAtIndex:0] == 0xff10) { + unichar chars[] = {0xd83c, 0xdd00, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } else { + unichar chars[] = {[label characterAtIndex:0] - 0xff11 + 0x2488, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } + } else if ([format containsString:@"%c,"]) { //🄂..🄊🄁 + newFormat = [format stringByReplacingOccurrencesOfString:@"%c," withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff10 + 0xdd01, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } + } else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff21, 26)] + isSupersetOfSet:labelCharacters]) { // A..Z + if ([format containsString:@"%c\u20dd"]) { // Ⓐ..Ⓩ + newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20dd" withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] - 0xff21 + 0x24b6, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } else if ([format containsString:@"(%c)"]) { // 🄐..🄩 + newFormat = [format stringByReplacingOccurrencesOfString:@"(%c)" withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd10, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } else if ([format containsString:@"%c\u20de"]) { // 🄰..🅉 + newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20de" withString:@"%S"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd30, 0x0}; + [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; + } + } + } + if (!newFormat) { + newFormat = [format stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; + for (NSString *label in labels) { + [formatted addObject:[NSString stringWithFormat:newFormat, label]]; + } + } + return formatted; +} + +NSMutableAttributedString * markDownText(NSMutableAttributedString* text) { + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; + NSUInteger __block offset = 0; + [regex enumerateMatchesInString:text.mutableString options:0 range:NSMakeRange(0, text.length) + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + result = [result resultByAdjustingRangesWithOffset:offset]; + NSString *tag = [text.mutableString substringWithRange:[result rangeAtIndex:1]]; + if ([tag isEqualToString:@"**"] || [tag isEqualToString:@"__"] || + [tag isEqualToString:@""] || [tag isEqualToString:@""]) { + [text applyFontTraits:NSBoldFontMask range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"*"] || [tag isEqualToString:@"_"] || + [tag isEqualToString:@""] || [tag isEqualToString:@""]) { + [text applyFontTraits:NSItalicFontMask range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@""]) { + [text addAttribute:NSUnderlineStyleAttributeName + value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"~~"] || [tag isEqualToString:@""]) { + [text addAttribute:NSStrikethroughStyleAttributeName + value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { + [text superscriptRange:[result rangeAtIndex:5]]; + [text enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 + usingBlock:^(id value, NSRange range, BOOL *stop) { + NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + [text addAttribute:NSFontAttributeName value:font range:range]; + }]; + } else if ([tag isEqualToString:@"~"] || [tag isEqualToString:@""]) { + [text subscriptRange:[result rangeAtIndex:5]]; + [text enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 + usingBlock:^(id value, NSRange range, BOOL *stop) { + NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + [text addAttribute:NSFontAttributeName value:font range:range]; + }]; + } + [text deleteCharactersInRange:[result rangeAtIndex:6]]; + [text deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:1].length; + }]; + if (offset == 0) { // no match. text remain unchanged. + return text; + } else { + return markDownText(text); + } +} + // Main function to add attributes to text output from librime - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange @@ -1784,6 +1913,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); NSMutableParagraphStyle *paragraphStyleCandidate; + NSArray *prefixLabels, *suffixLabels; // preedit if (preedit) { @@ -1808,9 +1938,8 @@ - (void)showPreedit:(NSString *)preedit } // force caret to be rendered horizontally in vertical layout if (caretPos != NSNotFound) { - [preeditLine addAttribute:NSVerticalGlyphFormAttributeName - value:@NO - range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; + [preeditLine addAttributes:@{NSVerticalGlyphFormAttributeName: @NO} + range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; @@ -1830,6 +1959,12 @@ - (void)showPreedit:(NSString *)preedit if (theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle copy]; } + if (theme.prefixLabelFormat != nil) { + prefixLabels = formatLabels(theme.prefixLabelFormat, labels); + } + if (theme.suffixLabelFormat != nil) { + suffixLabels = formatLabels(theme.suffixLabelFormat, labels); + } for (NSUInteger i = 0; i < candidates.count; ++i) { NSMutableAttributedString *item = [[NSMutableAttributedString alloc] init]; NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; @@ -1838,12 +1973,10 @@ - (void)showPreedit:(NSString *)preedit CGFloat labelWidth = 0.0; if (theme.prefixLabelFormat != nil) { - NSString *labelFormat = [theme.prefixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - NSString *prefixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:prefixLabelString attributes:labelAttrs]]; + initWithString:prefixLabels[i] attributes:labelAttrs]]; if (!theme.linear) { // get the label size for indent - labelWidth = item.size.width; + labelWidth = markDownText(item).size.width; } } @@ -1851,8 +1984,7 @@ - (void)showPreedit:(NSString *)preedit [item appendAttributedString:[[NSAttributedString alloc] initWithString:candidates[i] attributes:attrs]]; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - [item addAttribute:NSWritingDirectionAttributeName - value:@[@0] + [item addAttribute:NSWritingDirectionAttributeName value:@[@0] range:NSMakeRange(candidateStart, item.length - candidateStart)]; if (i < comments.count && [comments[i] length] != 0) { @@ -1862,30 +1994,28 @@ - (void)showPreedit:(NSString *)preedit } if (theme.suffixLabelFormat != nil) { - NSString *labelFormat = [theme.suffixLabelFormat stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - NSString *suffixLabelString = [NSString stringWithFormat:labelFormat, labels[i]]; [item appendAttributedString:[[NSAttributedString alloc] - initWithString:suffixLabelString attributes:labelAttrs]]; + initWithString:suffixLabels[i] attributes:labelAttrs]]; } if (!theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.headIndent = labelWidth; } - [item addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, item.length)]; + [markDownText(item) addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleCandidate + range:NSMakeRange(0, item.length)]; // determine if the line is too wide and line break is needed, based on screen size. if (i > 0) { NSUInteger separatorStart = text.length; - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? @" \t" : @" " ) : @"\n" attributes:theme.attrs]; + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.attrs]; [separator addAttribute:NSVerticalGlyphFormAttributeName value:@NO range:NSMakeRange(0, separator.length)]; NSRange separatorRange = NSMakeRange(separatorStart, separator.length); [text appendAttributedString:separator]; [text appendAttributedString:item]; if (theme.linear && [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { - [text replaceCharactersInRange:separatorRange withString:theme.tabled ? @" \n" : @"\n"]; + [text replaceCharactersInRange:separatorRange withString:theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\n"] : @"\n"]; lineStart = separatorStart + (theme.tabled ? 2 : 1); } } else { @@ -1903,18 +2033,18 @@ - (void)showPreedit:(NSString *)preedit [paging appendAttributedString:[[NSAttributedString alloc] initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]]; if (_turnPage == NSPageUpFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(0, 2)]; + [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(0, 1)]; } else if (_turnPage == NSPageDownFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 2, 2)]; + [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 1, 1)]; } - [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? @" \t" : @" ") : @"\n" attributes:theme.attrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.attrs]]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; [text appendAttributedString:paging]; if (theme.linear) { if ([self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:theme.tabled ? @"\n\t" : @"\n "]; + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:theme.tabled ? @"\n\t" : [@"\n" stringByAppendingString:kFullWidthSpace]]; lineStart = pagingStart; pagingStart += 1; } @@ -1940,8 +2070,8 @@ - (void)showPreedit:(NSString *)preedit } else { NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart + 2, 1) withString:@"\t"]; - [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 3, 1) withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1) withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) withString:@"\t"]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength / 2], [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; } @@ -1958,7 +2088,7 @@ - (void)showPreedit:(NSString *)preedit [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; if (preedit) { [self setLayoutForRange:preeditRange - withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) + withReferenceFont:(theme.vertical ? [theme.preeditAttrs[NSFontAttributeName] verticalFont] : theme.preeditAttrs[NSFontAttributeName]) paragraphStyle:theme.preeditParagraphStyle]; } if (numCandidates > 0) { @@ -2008,7 +2138,7 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages - CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:@" " attributes:theme.statusAttrs].size.width; + CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:kFullWidthSpace attributes:theme.statusAttrs].size.width; NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2, theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2); _view.textView.layoutOrientation = theme.vertical ? @@ -2018,10 +2148,9 @@ - (void)showStatus:(NSString *)message { [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; [text addAttribute:NSStrokeWidthAttributeName value:@(1 - self.backingScaleFactor) range:NSMakeRange(0, text.length)]; - [text fixAttributesInRange:NSMakeRange(0, text.length)]; + [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; [self setLayoutForRange:NSMakeRange(0, text.length) - withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : - theme.statusAttrs[NSFontAttributeName]) + withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) paragraphStyle:theme.statusParagraphStyle]; if (_statusTimer) { @@ -2073,7 +2202,6 @@ - (void)hideStatus:(NSTimer *)timer { if (fullname == nil) { return nil; } - NSArray *fontNames = [fullname componentsSeparatedByString:@","]; NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] initWithCapacity:fontNames.count]; for (__strong NSString *fontName in fontNames) { @@ -2091,13 +2219,10 @@ - (void)hideStatus:(NSTimer *)timer { return nil; } NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - CTFontRef systemFontRef = CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, 0.0, (CFStringRef)@"zh"); - NSFontDescriptor *systemFontDescriptor = CFBridgingRelease(CTFontCopyFontDescriptor(systemFontRef)); - CFRelease(systemFontRef); NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] - arrayByAddingObjectsFromArray:@[systemFontDescriptor, emojiFontDescriptor]]; + arrayByAddingObject:emojiFontDescriptor]; NSDictionary *attributes = @{NSFontCascadeListAttribute: fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } @@ -2379,7 +2504,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f CGFloat labelFontHeight = getLineHeight(labelFont, vertical); CGFloat commentFontHeight = getLineHeight(commentFont, vertical); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); - CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:@" " attributes:@{NSFontAttributeName: font}].size.width; + CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:kFullWidthSpace attributes:@{NSFontAttributeName: font}].size.width; NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; preeditParagraphStyle.minimumLineHeight = fontHeight; @@ -2425,11 +2550,16 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - CGFloat labelMidlineOffset = vertical ? 0.0 : floor(font.ascender / 2 + font.descender / 2 - labelFont.ascender / 2 - labelFont.descender / 2); + NSFont *refFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)font, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + labelAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); + labelHighlightedAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); + labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; + labelHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; + attrs[NSBaselineOffsetAttributeName] = @(baseOffset); highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelMidlineOffset); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset + labelMidlineOffset); + labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 13c8def57..9431eb6ea 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -42,7 +42,7 @@ style: # line length of preedit and candidates (fixed if value > 0, flexible/auto-adjust otherwise) line_length: 0 - #candidate_format: '%c %@' + #candidate_format: '%c. %@' # adjust the base line of vertical text #base_offset: 6 diff --git a/librime b/librime index 897bc7e7e..a9abcfe07 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 897bc7e7eca705d93d45d592c1ffa3ffbc1c246e +Subproject commit a9abcfe078d0d198252d35dc5683de978f304e13 diff --git a/macos_keycode.h b/macos_keycode.h index e24e44375..c68b0e772 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -3,12 +3,12 @@ #define _MACOS_KEYCODE_H_ // masks -#define OSX_CAPITAL_MASK 1 << 16 -#define OSX_SHIFT_MASK 1 << 17 -#define OSX_CTRL_MASK 1 << 18 -#define OSX_ALT_MASK 1 << 19 -#define OSX_COMMAND_MASK 1 << 20 -#define OSX_FN_MASK 1 << 23 +#define OSX_CAPITAL_MASK 1 << 16 +#define OSX_SHIFT_MASK 1 << 17 +#define OSX_CTRL_MASK 1 << 18 +#define OSX_ALT_MASK 1 << 19 +#define OSX_COMMAND_MASK 1 << 20 +#define OSX_FN_MASK 1 << 23 // key codes // @@ -18,218 +18,218 @@ // ---------------------------------------- // alphabet -#define OSX_VK_A 0x0 -#define OSX_VK_B 0xb -#define OSX_VK_C 0x8 -#define OSX_VK_D 0x2 -#define OSX_VK_E 0xe -#define OSX_VK_F 0x3 -#define OSX_VK_G 0x5 -#define OSX_VK_H 0x4 -#define OSX_VK_I 0x22 -#define OSX_VK_J 0x26 -#define OSX_VK_K 0x28 -#define OSX_VK_L 0x25 -#define OSX_VK_M 0x2e -#define OSX_VK_N 0x2d -#define OSX_VK_O 0x1f -#define OSX_VK_P 0x23 -#define OSX_VK_Q 0xc -#define OSX_VK_R 0xf -#define OSX_VK_S 0x1 -#define OSX_VK_T 0x11 -#define OSX_VK_U 0x20 -#define OSX_VK_V 0x9 -#define OSX_VK_W 0xd -#define OSX_VK_X 0x7 -#define OSX_VK_Y 0x10 -#define OSX_VK_Z 0x6 +#define OSX_VK_A 0x0 +#define OSX_VK_B 0xb +#define OSX_VK_C 0x8 +#define OSX_VK_D 0x2 +#define OSX_VK_E 0xe +#define OSX_VK_F 0x3 +#define OSX_VK_G 0x5 +#define OSX_VK_H 0x4 +#define OSX_VK_I 0x22 +#define OSX_VK_J 0x26 +#define OSX_VK_K 0x28 +#define OSX_VK_L 0x25 +#define OSX_VK_M 0x2e +#define OSX_VK_N 0x2d +#define OSX_VK_O 0x1f +#define OSX_VK_P 0x23 +#define OSX_VK_Q 0xc +#define OSX_VK_R 0xf +#define OSX_VK_S 0x1 +#define OSX_VK_T 0x11 +#define OSX_VK_U 0x20 +#define OSX_VK_V 0x9 +#define OSX_VK_W 0xd +#define OSX_VK_X 0x7 +#define OSX_VK_Y 0x10 +#define OSX_VK_Z 0x6 // ---------------------------------------- // number -#define OSX_VK_KEY_0 0x1d -#define OSX_VK_KEY_1 0x12 -#define OSX_VK_KEY_2 0x13 -#define OSX_VK_KEY_3 0x14 -#define OSX_VK_KEY_4 0x15 -#define OSX_VK_KEY_5 0x17 -#define OSX_VK_KEY_6 0x16 -#define OSX_VK_KEY_7 0x1a -#define OSX_VK_KEY_8 0x1c -#define OSX_VK_KEY_9 0x19 +#define OSX_VK_KEY_0 0x1d +#define OSX_VK_KEY_1 0x12 +#define OSX_VK_KEY_2 0x13 +#define OSX_VK_KEY_3 0x14 +#define OSX_VK_KEY_4 0x15 +#define OSX_VK_KEY_5 0x17 +#define OSX_VK_KEY_6 0x16 +#define OSX_VK_KEY_7 0x1a +#define OSX_VK_KEY_8 0x1c +#define OSX_VK_KEY_9 0x19 // ---------------------------------------- // symbol // BACKQUOTE is also known as grave accent or backtick. -#define OSX_VK_BACKQUOTE 0x32 -#define OSX_VK_BACKSLASH 0x2a -#define OSX_VK_BRACKET_LEFT 0x21 -#define OSX_VK_BRACKET_RIGHT 0x1e -#define OSX_VK_COMMA 0x2b -#define OSX_VK_DOT 0x2f -#define OSX_VK_EQUAL 0x18 -#define OSX_VK_MINUS 0x1b -#define OSX_VK_QUOTE 0x27 -#define OSX_VK_SEMICOLON 0x29 -#define OSX_VK_SLASH 0x2c +#define OSX_VK_BACKQUOTE 0x32 +#define OSX_VK_BACKSLASH 0x2a +#define OSX_VK_BRACKET_LEFT 0x21 +#define OSX_VK_BRACKET_RIGHT 0x1e +#define OSX_VK_COMMA 0x2b +#define OSX_VK_DOT 0x2f +#define OSX_VK_EQUAL 0x18 +#define OSX_VK_MINUS 0x1b +#define OSX_VK_QUOTE 0x27 +#define OSX_VK_SEMICOLON 0x29 +#define OSX_VK_SLASH 0x2c // ---------------------------------------- // keypad -#define OSX_VK_KEYPAD_0 0x52 -#define OSX_VK_KEYPAD_1 0x53 -#define OSX_VK_KEYPAD_2 0x54 -#define OSX_VK_KEYPAD_3 0x55 -#define OSX_VK_KEYPAD_4 0x56 -#define OSX_VK_KEYPAD_5 0x57 -#define OSX_VK_KEYPAD_6 0x58 -#define OSX_VK_KEYPAD_7 0x59 -#define OSX_VK_KEYPAD_8 0x5b -#define OSX_VK_KEYPAD_9 0x5c -#define OSX_VK_KEYPAD_CLEAR 0x47 -#define OSX_VK_KEYPAD_COMMA 0x5f -#define OSX_VK_KEYPAD_DOT 0x41 -#define OSX_VK_KEYPAD_EQUAL 0x51 -#define OSX_VK_KEYPAD_MINUS 0x4e -#define OSX_VK_KEYPAD_MULTIPLY 0x43 -#define OSX_VK_KEYPAD_PLUS 0x45 -#define OSX_VK_KEYPAD_SLASH 0x4b +#define OSX_VK_KEYPAD_0 0x52 +#define OSX_VK_KEYPAD_1 0x53 +#define OSX_VK_KEYPAD_2 0x54 +#define OSX_VK_KEYPAD_3 0x55 +#define OSX_VK_KEYPAD_4 0x56 +#define OSX_VK_KEYPAD_5 0x57 +#define OSX_VK_KEYPAD_6 0x58 +#define OSX_VK_KEYPAD_7 0x59 +#define OSX_VK_KEYPAD_8 0x5b +#define OSX_VK_KEYPAD_9 0x5c +#define OSX_VK_KEYPAD_CLEAR 0x47 +#define OSX_VK_KEYPAD_COMMA 0x5f +#define OSX_VK_KEYPAD_DOT 0x41 +#define OSX_VK_KEYPAD_EQUAL 0x51 +#define OSX_VK_KEYPAD_MINUS 0x4e +#define OSX_VK_KEYPAD_MULTIPLY 0x43 +#define OSX_VK_KEYPAD_PLUS 0x45 +#define OSX_VK_KEYPAD_SLASH 0x4b // ---------------------------------------- // special -#define OSX_VK_DELETE 0x33 -#define OSX_VK_ENTER 0x4c -#define OSX_VK_ENTER_POWERBOOK 0x34 -#define OSX_VK_ESCAPE 0x35 -#define OSX_VK_FORWARD_DELETE 0x75 -#define OSX_VK_HELP 0x72 -#define OSX_VK_RETURN 0x24 -#define OSX_VK_SPACE 0x31 -#define OSX_VK_TAB 0x30 +#define OSX_VK_DELETE 0x33 +#define OSX_VK_ENTER 0x4c +#define OSX_VK_ENTER_POWERBOOK 0x34 +#define OSX_VK_ESCAPE 0x35 +#define OSX_VK_FORWARD_DELETE 0x75 +#define OSX_VK_HELP 0x72 +#define OSX_VK_RETURN 0x24 +#define OSX_VK_SPACE 0x31 +#define OSX_VK_TAB 0x30 // ---------------------------------------- // function -#define OSX_VK_F1 0x7a -#define OSX_VK_F2 0x78 -#define OSX_VK_F3 0x63 -#define OSX_VK_F4 0x76 -#define OSX_VK_F5 0x60 -#define OSX_VK_F6 0x61 -#define OSX_VK_F7 0x62 -#define OSX_VK_F8 0x64 -#define OSX_VK_F9 0x65 -#define OSX_VK_F10 0x6d -#define OSX_VK_F11 0x67 -#define OSX_VK_F12 0x6f -#define OSX_VK_F13 0x69 -#define OSX_VK_F14 0x6b -#define OSX_VK_F15 0x71 -#define OSX_VK_F16 0x6a -#define OSX_VK_F17 0x40 -#define OSX_VK_F18 0x4f -#define OSX_VK_F19 0x50 +#define OSX_VK_F1 0x7a +#define OSX_VK_F2 0x78 +#define OSX_VK_F3 0x63 +#define OSX_VK_F4 0x76 +#define OSX_VK_F5 0x60 +#define OSX_VK_F6 0x61 +#define OSX_VK_F7 0x62 +#define OSX_VK_F8 0x64 +#define OSX_VK_F9 0x65 +#define OSX_VK_F10 0x6d +#define OSX_VK_F11 0x67 +#define OSX_VK_F12 0x6f +#define OSX_VK_F13 0x69 +#define OSX_VK_F14 0x6b +#define OSX_VK_F15 0x71 +#define OSX_VK_F16 0x6a +#define OSX_VK_F17 0x40 +#define OSX_VK_F18 0x4f +#define OSX_VK_F19 0x50 // ---------------------------------------- // functional -#define OSX_VK_BRIGHTNESS_DOWN 0x91 -#define OSX_VK_BRIGHTNESS_UP 0x90 -#define OSX_VK_DASHBOARD 0x82 -#define OSX_VK_EXPOSE_ALL 0xa0 -#define OSX_VK_LAUNCHPAD 0x83 -#define OSX_VK_MISSION_CONTROL 0xa0 +#define OSX_VK_BRIGHTNESS_DOWN 0x91 +#define OSX_VK_BRIGHTNESS_UP 0x90 +#define OSX_VK_DASHBOARD 0x82 +#define OSX_VK_EXPOSE_ALL 0xa0 +#define OSX_VK_LAUNCHPAD 0x83 +#define OSX_VK_MISSION_CONTROL 0xa0 // ---------------------------------------- // cursor -#define OSX_VK_CURSOR_UP 0x7e -#define OSX_VK_CURSOR_DOWN 0x7d -#define OSX_VK_CURSOR_LEFT 0x7b -#define OSX_VK_CURSOR_RIGHT 0x7c +#define OSX_VK_CURSOR_UP 0x7e +#define OSX_VK_CURSOR_DOWN 0x7d +#define OSX_VK_CURSOR_LEFT 0x7b +#define OSX_VK_CURSOR_RIGHT 0x7c -#define OSX_VK_PAGEUP 0x74 -#define OSX_VK_PAGEDOWN 0x79 -#define OSX_VK_HOME 0x73 -#define OSX_VK_END 0x77 +#define OSX_VK_PAGEUP 0x74 +#define OSX_VK_PAGEDOWN 0x79 +#define OSX_VK_HOME 0x73 +#define OSX_VK_END 0x77 // ---------------------------------------- // modifiers -#define OSX_VK_CAPSLOCK 0x39 -#define OSX_VK_COMMAND_L 0x37 -#define OSX_VK_COMMAND_R 0x36 -#define OSX_VK_CONTROL_L 0x3b -#define OSX_VK_CONTROL_R 0x3e -#define OSX_VK_FN 0x3f -#define OSX_VK_OPTION_L 0x3a -#define OSX_VK_OPTION_R 0x3d -#define OSX_VK_SHIFT_L 0x38 -#define OSX_VK_SHIFT_R 0x3c +#define OSX_VK_CAPSLOCK 0x39 +#define OSX_VK_COMMAND_L 0x37 +#define OSX_VK_COMMAND_R 0x36 +#define OSX_VK_CONTROL_L 0x3b +#define OSX_VK_CONTROL_R 0x3e +#define OSX_VK_FN 0x3f +#define OSX_VK_OPTION_L 0x3a +#define OSX_VK_OPTION_R 0x3d +#define OSX_VK_SHIFT_L 0x38 +#define OSX_VK_SHIFT_R 0x3c // ---------------------------------------- // pc keyboard -#define OSX_VK_PC_APPLICATION 0x6e -#define OSX_VK_PC_BS 0x33 -#define OSX_VK_PC_DEL 0x75 -#define OSX_VK_PC_INSERT 0x72 -#define OSX_VK_PC_KEYPAD_NUMLOCK 0x47 -#define OSX_VK_PC_PAUSE 0x71 -#define OSX_VK_PC_POWER 0x7f -#define OSX_VK_PC_PRINTSCREEN 0x69 -#define OSX_VK_PC_SCROLLLOCK 0x6b +#define OSX_VK_PC_APPLICATION 0x6e +#define OSX_VK_PC_BS 0x33 +#define OSX_VK_PC_DEL 0x75 +#define OSX_VK_PC_INSERT 0x72 +#define OSX_VK_PC_KEYPAD_NUMLOCK 0x47 +#define OSX_VK_PC_PAUSE 0x71 +#define OSX_VK_PC_POWER 0x7f +#define OSX_VK_PC_PRINTSCREEN 0x69 +#define OSX_VK_PC_SCROLLLOCK 0x6b // ---------------------------------------- // international -#define OSX_VK_DANISH_DOLLAR 0xa -#define OSX_VK_DANISH_LESS_THAN 0x32 - -#define OSX_VK_FRENCH_DOLLAR 0x1e -#define OSX_VK_FRENCH_EQUAL 0x2c -#define OSX_VK_FRENCH_HAT 0x21 -#define OSX_VK_FRENCH_MINUS 0x18 -#define OSX_VK_FRENCH_RIGHT_PAREN 0x1b - -#define OSX_VK_GERMAN_CIRCUMFLEX 0xa -#define OSX_VK_GERMAN_LESS_THAN 0x32 -#define OSX_VK_GERMAN_PC_LESS_THAN 0x80 -#define OSX_VK_GERMAN_QUOTE 0x18 -#define OSX_VK_GERMAN_A_UMLAUT 0x27 -#define OSX_VK_GERMAN_O_UMLAUT 0x29 -#define OSX_VK_GERMAN_U_UMLAUT 0x21 - -#define OSX_VK_ITALIAN_BACKSLASH 0xa -#define OSX_VK_ITALIAN_LESS_THAN 0x32 - -#define OSX_VK_JIS_ATMARK 0x21 -#define OSX_VK_JIS_BRACKET_LEFT 0x1e -#define OSX_VK_JIS_BRACKET_RIGHT 0x2a -#define OSX_VK_JIS_COLON 0x27 -#define OSX_VK_JIS_DAKUON 0x21 -#define OSX_VK_JIS_EISUU 0x66 -#define OSX_VK_JIS_HANDAKUON 0x1e -#define OSX_VK_JIS_HAT 0x18 -#define OSX_VK_JIS_KANA 0x68 -#define OSX_VK_JIS_PC_HAN_ZEN 0x32 -#define OSX_VK_JIS_UNDERSCORE 0x5e -#define OSX_VK_JIS_YEN 0x5d - -#define OSX_VK_RUSSIAN_PARAGRAPH 0xa -#define OSX_VK_RUSSIAN_TILDE 0x32 +#define OSX_VK_DANISH_DOLLAR 0xa +#define OSX_VK_DANISH_LESS_THAN 0x32 + +#define OSX_VK_FRENCH_DOLLAR 0x1e +#define OSX_VK_FRENCH_EQUAL 0x2c +#define OSX_VK_FRENCH_HAT 0x21 +#define OSX_VK_FRENCH_MINUS 0x18 +#define OSX_VK_FRENCH_RIGHT_PAREN 0x1b + +#define OSX_VK_GERMAN_CIRCUMFLEX 0xa +#define OSX_VK_GERMAN_LESS_THAN 0x32 +#define OSX_VK_GERMAN_PC_LESS_THAN 0x80 +#define OSX_VK_GERMAN_QUOTE 0x18 +#define OSX_VK_GERMAN_A_UMLAUT 0x27 +#define OSX_VK_GERMAN_O_UMLAUT 0x29 +#define OSX_VK_GERMAN_U_UMLAUT 0x21 + +#define OSX_VK_ITALIAN_BACKSLASH 0xa +#define OSX_VK_ITALIAN_LESS_THAN 0x32 + +#define OSX_VK_JIS_ATMARK 0x21 +#define OSX_VK_JIS_BRACKET_LEFT 0x1e +#define OSX_VK_JIS_BRACKET_RIGHT 0x2a +#define OSX_VK_JIS_COLON 0x27 +#define OSX_VK_JIS_DAKUON 0x21 +#define OSX_VK_JIS_EISUU 0x66 +#define OSX_VK_JIS_HANDAKUON 0x1e +#define OSX_VK_JIS_HAT 0x18 +#define OSX_VK_JIS_KANA 0x68 +#define OSX_VK_JIS_PC_HAN_ZEN 0x32 +#define OSX_VK_JIS_UNDERSCORE 0x5e +#define OSX_VK_JIS_YEN 0x5d + +#define OSX_VK_RUSSIAN_PARAGRAPH 0xa +#define OSX_VK_RUSSIAN_TILDE 0x32 #define OSX_VK_SPANISH_LESS_THAN 0x32 #define OSX_VK_SPANISH_ORDINAL_INDICATOR 0xa -#define OSX_VK_SWEDISH_LESS_THAN 0x32 -#define OSX_VK_SWEDISH_SECTION 0xa +#define OSX_VK_SWEDISH_LESS_THAN 0x32 +#define OSX_VK_SWEDISH_SECTION 0xa -#define OSX_VK_SWISS_LESS_THAN 0x32 -#define OSX_VK_SWISS_SECTION 0xa +#define OSX_VK_SWISS_LESS_THAN 0x32 +#define OSX_VK_SWISS_SECTION 0xa -#define OSX_VK_UK_SECTION 0xa +#define OSX_VK_UK_SECTION 0xa // conversion functions diff --git a/macos_keycode.m b/macos_keycode.m index 1e54ef106..32eefe081 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -3,10 +3,9 @@ #import -int osx_modifiers_to_rime_modifiers(unsigned long modifiers) -{ +int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { int ret = 0; - + if (modifiers & OSX_CAPITAL_MASK) ret |= kLockMask; if (modifiers & OSX_SHIFT_MASK) @@ -19,7 +18,7 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) ret |= kSuperMask; if (modifiers & OSX_FN_MASK) ret |= kHyperMask; - + return ret; } @@ -27,93 +26,92 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) int osx_keycode, rime_keycode; } keycode_mappings[] = { // modifiers - {OSX_VK_CAPSLOCK, XK_Caps_Lock}, - {OSX_VK_COMMAND_L, XK_Super_L}, // XK_Meta_L? - {OSX_VK_COMMAND_R, XK_Super_R}, // XK_Meta_R? - {OSX_VK_CONTROL_L, XK_Control_L}, - {OSX_VK_CONTROL_R, XK_Control_R}, - {OSX_VK_FN, XK_Hyper_L}, - {OSX_VK_OPTION_L, XK_Alt_L}, - {OSX_VK_OPTION_R, XK_Alt_R}, - {OSX_VK_SHIFT_L, XK_Shift_L}, - {OSX_VK_SHIFT_R, XK_Shift_R}, + { OSX_VK_CAPSLOCK, XK_Caps_Lock }, + { OSX_VK_COMMAND_L, XK_Super_L }, // XK_Meta_L? + { OSX_VK_COMMAND_R, XK_Super_R }, // XK_Meta_R? + { OSX_VK_CONTROL_L, XK_Control_L }, + { OSX_VK_CONTROL_R, XK_Control_R }, + { OSX_VK_FN, XK_Hyper_L }, + { OSX_VK_OPTION_L, XK_Alt_L }, + { OSX_VK_OPTION_R, XK_Alt_R }, + { OSX_VK_SHIFT_L, XK_Shift_L }, + { OSX_VK_SHIFT_R, XK_Shift_R }, // special - {OSX_VK_DELETE, XK_BackSpace}, - {OSX_VK_ENTER, XK_KP_Enter}, + { OSX_VK_DELETE, XK_BackSpace }, + { OSX_VK_ENTER, XK_KP_Enter }, //OSX_VK_ENTER_POWERBOOK -> ? - {OSX_VK_ESCAPE, XK_Escape}, - {OSX_VK_FORWARD_DELETE, XK_Delete}, + { OSX_VK_ESCAPE, XK_Escape }, + { OSX_VK_FORWARD_DELETE, XK_Delete }, //{OSX_VK_HELP, XK_Help}, // the same keycode with OSX_VK_PC_INSERT - {OSX_VK_RETURN, XK_Return}, - {OSX_VK_SPACE, XK_space}, - {OSX_VK_TAB, XK_Tab}, + { OSX_VK_RETURN, XK_Return }, + { OSX_VK_SPACE, XK_space }, + { OSX_VK_TAB, XK_Tab }, // function - {OSX_VK_F1, XK_F1}, - {OSX_VK_F2, XK_F2}, - {OSX_VK_F3, XK_F3}, - {OSX_VK_F4, XK_F4}, - {OSX_VK_F5, XK_F5}, - {OSX_VK_F6, XK_F6}, - {OSX_VK_F7, XK_F7}, - {OSX_VK_F8, XK_F8}, - {OSX_VK_F9, XK_F9}, - {OSX_VK_F10, XK_F10}, - {OSX_VK_F11, XK_F11}, - {OSX_VK_F12, XK_F12}, - {OSX_VK_F13, XK_F13}, - {OSX_VK_F14, XK_F14}, - {OSX_VK_F15, XK_F15}, - {OSX_VK_F16, XK_F16}, - {OSX_VK_F17, XK_F17}, - {OSX_VK_F18, XK_F18}, - {OSX_VK_F19, XK_F19}, + { OSX_VK_F1, XK_F1 }, + { OSX_VK_F2, XK_F2 }, + { OSX_VK_F3, XK_F3 }, + { OSX_VK_F4, XK_F4 }, + { OSX_VK_F5, XK_F5 }, + { OSX_VK_F6, XK_F6 }, + { OSX_VK_F7, XK_F7 }, + { OSX_VK_F8, XK_F8 }, + { OSX_VK_F9, XK_F9 }, + { OSX_VK_F10, XK_F10 }, + { OSX_VK_F11, XK_F11 }, + { OSX_VK_F12, XK_F12 }, + { OSX_VK_F13, XK_F13 }, + { OSX_VK_F14, XK_F14 }, + { OSX_VK_F15, XK_F15 }, + { OSX_VK_F16, XK_F16 }, + { OSX_VK_F17, XK_F17 }, + { OSX_VK_F18, XK_F18 }, + { OSX_VK_F19, XK_F19 }, // cursor - {OSX_VK_CURSOR_UP, XK_Up}, - {OSX_VK_CURSOR_DOWN, XK_Down}, - {OSX_VK_CURSOR_LEFT, XK_Left}, - {OSX_VK_CURSOR_RIGHT, XK_Right}, - {OSX_VK_PAGEUP, XK_Page_Up}, - {OSX_VK_PAGEDOWN, XK_Page_Down}, - {OSX_VK_HOME, XK_Home}, - {OSX_VK_END, XK_End}, + { OSX_VK_CURSOR_UP, XK_Up }, + { OSX_VK_CURSOR_DOWN, XK_Down }, + { OSX_VK_CURSOR_LEFT, XK_Left }, + { OSX_VK_CURSOR_RIGHT, XK_Right }, + { OSX_VK_PAGEUP, XK_Page_Up }, + { OSX_VK_PAGEDOWN, XK_Page_Down }, + { OSX_VK_HOME, XK_Home }, + { OSX_VK_END, XK_End }, // keypad - {OSX_VK_KEYPAD_0, XK_KP_0}, - {OSX_VK_KEYPAD_1, XK_KP_1}, - {OSX_VK_KEYPAD_2, XK_KP_2}, - {OSX_VK_KEYPAD_3, XK_KP_3}, - {OSX_VK_KEYPAD_4, XK_KP_4}, - {OSX_VK_KEYPAD_5, XK_KP_5}, - {OSX_VK_KEYPAD_6, XK_KP_6}, - {OSX_VK_KEYPAD_7, XK_KP_7}, - {OSX_VK_KEYPAD_8, XK_KP_8}, - {OSX_VK_KEYPAD_9, XK_KP_9}, - {OSX_VK_KEYPAD_CLEAR, XK_Clear}, - {OSX_VK_KEYPAD_COMMA, XK_KP_Separator}, - {OSX_VK_KEYPAD_DOT, XK_KP_Decimal}, - {OSX_VK_KEYPAD_EQUAL, XK_KP_Equal}, - {OSX_VK_KEYPAD_MINUS, XK_KP_Subtract}, - {OSX_VK_KEYPAD_MULTIPLY, XK_KP_Multiply}, - {OSX_VK_KEYPAD_PLUS, XK_KP_Add}, - {OSX_VK_KEYPAD_SLASH, XK_KP_Divide}, + { OSX_VK_KEYPAD_0, XK_KP_0 }, + { OSX_VK_KEYPAD_1, XK_KP_1 }, + { OSX_VK_KEYPAD_2, XK_KP_2 }, + { OSX_VK_KEYPAD_3, XK_KP_3 }, + { OSX_VK_KEYPAD_4, XK_KP_4 }, + { OSX_VK_KEYPAD_5, XK_KP_5 }, + { OSX_VK_KEYPAD_6, XK_KP_6 }, + { OSX_VK_KEYPAD_7, XK_KP_7 }, + { OSX_VK_KEYPAD_8, XK_KP_8 }, + { OSX_VK_KEYPAD_9, XK_KP_9 }, + { OSX_VK_KEYPAD_CLEAR, XK_Clear }, + { OSX_VK_KEYPAD_COMMA, XK_KP_Separator }, + { OSX_VK_KEYPAD_DOT, XK_KP_Decimal }, + { OSX_VK_KEYPAD_EQUAL, XK_KP_Equal }, + { OSX_VK_KEYPAD_MINUS, XK_KP_Subtract }, + { OSX_VK_KEYPAD_MULTIPLY, XK_KP_Multiply }, + { OSX_VK_KEYPAD_PLUS, XK_KP_Add }, + { OSX_VK_KEYPAD_SLASH, XK_KP_Divide }, // pc keyboard - {OSX_VK_PC_APPLICATION, XK_Menu}, - {OSX_VK_PC_INSERT, XK_Insert}, - {OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock}, - {OSX_VK_PC_PAUSE, XK_Pause}, + { OSX_VK_PC_APPLICATION, XK_Menu }, + { OSX_VK_PC_INSERT, XK_Insert }, + { OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock }, + { OSX_VK_PC_PAUSE, XK_Pause }, //OSX_VK_PC_POWER -> ? - {OSX_VK_PC_PRINTSCREEN, XK_Print}, - {OSX_VK_PC_SCROLLLOCK, XK_Scroll_Lock}, + { OSX_VK_PC_PRINTSCREEN, XK_Print }, + { OSX_VK_PC_SCROLLLOCK, XK_Scroll_Lock }, - {-1, -1} + { -1, -1 } }; -int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) -{ +int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) { for (struct keycode_mapping_t *mapping = keycode_mappings; mapping->osx_keycode >= 0; ++mapping) { @@ -134,4 +132,3 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) return XK_VoidSymbol; } - From 391551283b8e3ba85c96f40f5a99896d4f9fd2a8 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 4 Sep 2023 05:03:03 +0200 Subject: [PATCH 138/164] Update packages --- INSTALL.md | 2 +- Makefile | 10 +++++----- SquirrelInputController.m | 34 +++++++++++++++++----------------- autobuild.sh | 10 +++++++--- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index f15901bc5..07d6d2354 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 make -C librime xcode/deps/boost -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_82_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/Makefile b/Makefile index 98783ab2a..7db9bf8da 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ all: release install: install-release -# Change to `xcode/dist-with-icu` if boost is linked to icu libraries. -RIME_DIST_TARGET = xcode/dist +# Change to `dist-with-icu` if boost is linked to icu libraries. +RIME_DIST_TARGET = dist RIME_BIN_DIR = librime/dist/bin RIME_LIB_DIR = librime/dist/lib @@ -39,7 +39,7 @@ $(RIME_LIBRARY): $(MAKE) librime $(RIME_DEPS): - $(MAKE) -C librime xcode/deps + $(MAKE) -C librime deps librime: $(RIME_DEPS) $(MAKE) -C librime $(RIME_DIST_TARGET) @@ -67,7 +67,7 @@ plum-data: $(MAKE) copy-plum-data opencc-data: - $(MAKE) -C librime xcode/deps/opencc + $(MAKE) -C librime deps/opencc $(MAKE) copy-opencc-data copy-plum-data: @@ -170,5 +170,5 @@ clean: clean-deps: $(MAKE) -C plum clean - $(MAKE) -C librime xcode/clean + $(MAKE) -C librime clean $(MAKE) clean-sparkle diff --git a/SquirrelInputController.m b/SquirrelInputController.m index dd55f4518..58f880675 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -25,7 +25,7 @@ @implementation SquirrelInputController { NSUInteger _caretPos; NSArray *_candidates; NSUInteger _lastModifier; - int _lastEventCount; + uint _lastEventCount; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; @@ -36,7 +36,7 @@ @implementation SquirrelInputController { // for chord-typing int _chordKeyCodes[N_KEY_ROLL_OVER]; int _chordModifiers[N_KEY_ROLL_OVER]; - int _chordKeyCount; + uint _chordKeyCount; NSTimer *_chordTimer; NSTimeInterval _chordDuration; NSString *_currentApp; @@ -54,7 +54,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; + NSUInteger modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @autoreleasepool { @@ -82,13 +82,13 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - CGKeyCode keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); + ushort keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); - int eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + - CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + - CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDown) + - CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDown) + - CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); + uint eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDown) + + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); _lastModifier = modifiers; switch (keyCode) { case kVK_CapsLock: { @@ -132,7 +132,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender break; } - int keyCode = event.keyCode; + ushort keyCode = event.keyCode; NSString *keyChars = ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && !(modifiers & OSX_ALT_MASK)) ? event.characters : event.charactersIgnoringModifiers; //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", @@ -239,7 +239,7 @@ - (void)onChordTimer:(NSTimer *)timer int processed_keys = 0; if (_chordKeyCount && _session) { // simulate key-ups - for (int i = 0; i < _chordKeyCount; ++i) { + for (uint i = 0; i < _chordKeyCount; ++i) { if (rime_get_api()->process_key(_session, _chordKeyCodes[i], (_chordModifiers[i] | kReleaseMask))) { ++processed_keys; @@ -255,7 +255,7 @@ - (void)onChordTimer:(NSTimer *)timer - (void)updateChord:(int)keycode modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); - for (int i = 0; i < _chordKeyCount; ++i) { + for (uint i = 0; i < _chordKeyCount; ++i) { if (_chordKeyCodes[i] == keycode) { return; } @@ -625,7 +625,7 @@ - (void)rimeUpdate } else if (rime_get_api()->get_option(_session, "soft_cursor")) { int cursorPos = ctx.composition.cursor_pos; char composed[strlen(preedit) - 3]; - for (int i = 0; i < strlen(preedit) - 3; ++i) { + for (size_t i = 0; i < strlen(preedit) - 3; ++i) { composed[i] = preedit[i < cursorPos ? i : i + 3]; } _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; @@ -672,7 +672,7 @@ - (void)rimeUpdate // update candidates NSMutableArray *candidates = [NSMutableArray array]; NSMutableArray *comments = [NSMutableArray array]; - for (NSUInteger i = 0; i < ctx.menu.num_candidates; ++i) { + for (int i = 0; i < ctx.menu.num_candidates; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; if (ctx.menu.candidates[i].comment) { [comments addObject:@(ctx.menu.candidates[i].comment)]; @@ -683,16 +683,16 @@ - (void)rimeUpdate NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:ctx.menu.page_size]; if (ctx.menu.select_keys) { NSString *selectKeys = [@(ctx.menu.select_keys) stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; - for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + for (int i = 0; i < ctx.menu.page_size; ++i) { [labels addObject:[selectKeys substringWithRange:NSMakeRange(i, 1)]]; } } else if (ctx.select_labels) { - for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + for (int i = 0; i < ctx.menu.page_size; ++i) { [labels addObject:@(ctx.select_labels[i])]; } } else { NSString *labelString = @"1234567890"; - for (NSUInteger i = 0; i < ctx.menu.page_size; ++i) { + for (int i = 0; i < ctx.menu.page_size; ++i) { [labels addObject:[labelString substringWithRange:NSMakeRange(i, 1)]]; } } diff --git a/autobuild.sh b/autobuild.sh index 804933579..46fd396de 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -6,11 +6,15 @@ git submodules update --init --recursive export BUILD_UNIVERSAL=1 -make -C librime xcode/deps/boost +export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_82_0" +export CMAKE_GENERATOR=Ninja -export BUILD_UNIVERSAL=1 +bash librime/install-boost.sh + +make librime deps + +make librime install make deps From 2946c7502bef90ee51c56310afd2198d57f5a5b7 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 25 Sep 2023 03:38:59 +0200 Subject: [PATCH 139/164] fix terminal non-inline --- SquirrelInputController.m | 2 +- SquirrelPanel.m | 3 ++- data/squirrel.yaml | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 58f880675..6e8a055c0 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -145,7 +145,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) - if (keyCode >= 0x60 || keyCode == 0x50 || keyCode == 0x4f || + if ((keyCode <= 0xff && keyCode >= 0x60) || keyCode == 0x50 || keyCode == 0x4f || keyCode == 0x47 || keyCode == 0x40) { rime_modifiers ^= kHyperMask; } diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 3a8be2285..9c752b3df 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1204,7 +1204,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; - + defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); // solve terminal hijack when non-inline + NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; attrs[NSFontAttributeName] = userFont; diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 9431eb6ea..a18ef0abc 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -345,7 +345,6 @@ app_options: com.apple.Terminal: ascii_mode: true no_inline: true - inline_placeholder: true com.googlecode.iterm2: ascii_mode: true no_inline: true From aaebf9fe9c274802d94c61672252c0817351c43c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 31 Aug 2023 00:15:20 +0200 Subject: [PATCH 140/164] icon --- .../RimeIcon.appiconset/Contents.json | 20 +- .../{rime-512@2x.png => rime-1024.png} | Bin .../{rime-128@1x.png => rime-128.png} | Bin .../RimeIcon.appiconset/rime-128@2x.png | Bin 14335 -> 0 bytes .../{rime-16@1x.png => rime-16.png} | Bin .../RimeIcon.appiconset/rime-16@2x.png | Bin 2234 -> 0 bytes .../{rime-256@1x.png => rime-256.png} | Bin .../RimeIcon.appiconset/rime-256@2x.png | Bin 38775 -> 0 bytes .../{rime-32@1x.png => rime-32.png} | Bin .../{rime-512@1x.png => rime-512.png} | Bin .../{rime-32@2x.png => rime-64.png} | Bin Assets.xcassets/rime.imageset/Contents.json | 12 + Assets.xcassets/rime.imageset/rime.svg | 13 ++ Info.plist | 54 +++-- Squirrel.xcodeproj/project.pbxproj | 18 +- SquirrelInputController.m | 28 +-- SquirrelPanel.m | 216 +++++++++--------- rime.pdf | Bin 51874 -> 0 bytes rime.svg | 20 -- 19 files changed, 190 insertions(+), 191 deletions(-) rename Assets.xcassets/RimeIcon.appiconset/{rime-512@2x.png => rime-1024.png} (100%) rename Assets.xcassets/RimeIcon.appiconset/{rime-128@1x.png => rime-128.png} (100%) delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png rename Assets.xcassets/RimeIcon.appiconset/{rime-16@1x.png => rime-16.png} (100%) delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-16@2x.png rename Assets.xcassets/RimeIcon.appiconset/{rime-256@1x.png => rime-256.png} (100%) delete mode 100644 Assets.xcassets/RimeIcon.appiconset/rime-256@2x.png rename Assets.xcassets/RimeIcon.appiconset/{rime-32@1x.png => rime-32.png} (100%) rename Assets.xcassets/RimeIcon.appiconset/{rime-512@1x.png => rime-512.png} (100%) rename Assets.xcassets/RimeIcon.appiconset/{rime-32@2x.png => rime-64.png} (100%) create mode 100644 Assets.xcassets/rime.imageset/Contents.json create mode 100644 Assets.xcassets/rime.imageset/rime.svg delete mode 100644 rime.pdf delete mode 100644 rime.svg diff --git a/Assets.xcassets/RimeIcon.appiconset/Contents.json b/Assets.xcassets/RimeIcon.appiconset/Contents.json index 6583b127d..7c8a1bdd1 100644 --- a/Assets.xcassets/RimeIcon.appiconset/Contents.json +++ b/Assets.xcassets/RimeIcon.appiconset/Contents.json @@ -1,61 +1,61 @@ { "images" : [ { - "filename" : "rime-16@1x.png", + "filename" : "rime-16.png", "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { - "filename" : "rime-16@2x.png", + "filename" : "rime-32.png", "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { - "filename" : "rime-32@1x.png", + "filename" : "rime-32.png", "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { - "filename" : "rime-32@2x.png", + "filename" : "rime-64.png", "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { - "filename" : "rime-128@1x.png", + "filename" : "rime-128.png", "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { - "filename" : "rime-128@2x.png", + "filename" : "rime-256.png", "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { - "filename" : "rime-256@1x.png", + "filename" : "rime-256.png", "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { - "filename" : "rime-256@2x.png", + "filename" : "rime-512.png", "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { - "filename" : "rime-512@1x.png", + "filename" : "rime-512.png", "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { - "filename" : "rime-512@2x.png", + "filename" : "rime-1024.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-512@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-1024.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-512@2x.png rename to Assets.xcassets/RimeIcon.appiconset/rime-1024.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128@1x.png b/Assets.xcassets/RimeIcon.appiconset/rime-128.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-128@1x.png rename to Assets.xcassets/RimeIcon.appiconset/rime-128.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-128@2x.png deleted file mode 100644 index 924206df532e6fa7d956a4d46bc05c20441d3b0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14335 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 Wt{+0QTJra|Hq~dEPpcKILjD)A6r_d# diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16@1x.png b/Assets.xcassets/RimeIcon.appiconset/rime-16.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-16@1x.png rename to Assets.xcassets/RimeIcon.appiconset/rime-16.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-16@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-16@2x.png deleted file mode 100644 index 9786c463bcf25bd6e29b9a439bfb79a9cbdddcbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2234 zcmZ`)4LF#YE04df0Waa>{ z07IG6033D%V3q*@p$q^sK}CCr7aYieXcRV;3hZEC2WV>;0ZrJ`fN!8-0o2uC5BO_r zedmX2*skUv0LbM6t<{`J*s8Bn@KSSDtY)>wTE%L_yKGdo=9;gj$z<7V*dPUDx)1D$IpT46yaSx!AUu*FVu~FSgdeRG`ClDhNXSa!3PfCf0!m$%8O=W| z@#=qD z>iG!4TrtF>`Eui-1fhBwXSlP!3~VIs;q&-Op9q*Ni0HIRSqE**UY4Q%oy@wxD$xt4 z-tBd}zjCyeQTS{Tb-i%!&M8s%=$7fX@V8b@@%0I#A$uhq`=0JKy#1dlJ}KlyVeDyp zYnj4NFI7s$AWThbo22u6>5v~;@p4mAHZ3ytBiF2xnMicj`-bBxBFDMPgA@4LtMf&Y z)rV!jjhwrk%;qF3=RF7C+$&i+tJ1!3$M|)X^hkkj1=;E5WDr43t2~ z8vz+6xm7vcA7D*tjpzV#%}X~@|`?7uJS>o@gSBKxAP*e~z+9JTG(gtN7M9v>estEuT# zPEOW@??%+#ebHYsk&QEFh){OUV|VQu?~nVTxwCWhYHO># z^DzZ88&>^#A^q3aSm<8FJ}V$Yag|?wmws-XppHb6KflNh=>0U z{y9ignVOk-Hlc2o?mSRyvcD$=NiyMj-0iPCAA+UVOR{GecJmgH6~kUE77L`bfPM6+ zs4J7&7)$?33j4l7QGN8frXGGyVY5BNB`7GUp>A$2k;}dG ztMMZ8xT|$8dAp6ZwaN6t!o#X_=Z?_Nf8JeFW0fHm*AJ9^bA0nw9`eg8j6W<(vHny1 zec<-g6kB_GX68jfK|y+I>PS^(9d&nH4J20RXx}9QT zIGkp`O`^Fa@gKhyDr4>Zii?X=6y@dR&3AT29X$A;I9(ukG^JF&lB3l~0~ z4F`pUoJP=$o!Y$V;n-gWN4ng+yL=7`T&bzYkQWV+f%hLdJQyFp@}|P>Iew!5)i05B zBWGvlFVDz2JEuRZlk|)(-n~n0l}gVW*pahn6}inP5n2oR7YpUcWEP8+5EtiPywg^r zFtKIOl}H>6o39jV-oHivV|28_y5|V^?|fH)BIF#0M+~_o8fHt;>iwyQGuJ0m|GQdDa}kfA_udpUoMk` zd-qEMEo~x44%1SkA-eVq{0O2_(c4S4^vQ;}3rOJ(LpKFxF1r<4aY-^0?0jPwsSHy-OQ{aH_W=(oP7 aOKuT$T{V;G7s}QD$P`kb?>D;{S^ovb#b!4E diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256@1x.png b/Assets.xcassets/RimeIcon.appiconset/rime-256.png similarity index 100% rename from Assets.xcassets/RimeIcon.appiconset/rime-256@1x.png rename to Assets.xcassets/RimeIcon.appiconset/rime-256.png diff --git a/Assets.xcassets/RimeIcon.appiconset/rime-256@2x.png b/Assets.xcassets/RimeIcon.appiconset/rime-256@2x.png deleted file mode 100644 index 9eb0dcf50b3b9a7d24f82a58e51aa337f4927d03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38775 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

      XEl1PdeR(&W>e!RycY+^z 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 + + + + + + + + + diff --git a/Info.plist b/Info.plist index adf633d4f..fec7d1ba2 100644 --- a/Info.plist +++ b/Info.plist @@ -26,14 +26,17 @@ im.rime.inputmethod.Squirrel.Hant + TISIconLabels + + Primary + + TISInputSourceID im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage zh-Hant TISIconIsTemplate - tsInputModeAlternateMenuIconFileKey - rime.svg tsInputModeCharacterRepertoireKey Hant @@ -46,9 +49,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.svg - tsInputModePaletteIconFileKey - rime.svg + rime.imageset + tsInputModeAlternateMenuIconFileKey + rime.imageset tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -56,14 +59,17 @@ im.rime.inputmethod.Squirrel.Hans + TISIconLabels + + Primary + + TISInputSourceID im.rime.inputmethod.Squirrel.Hans TISIntendedLanguage zh-Hans TISIconIsTemplate - tsInputModeAlternateMenuIconFileKey - rime.svg tsInputModeCharacterRepertoireKey Hans @@ -76,9 +82,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.svg - tsInputModePaletteIconFileKey - rime.svg + rime.imageset + tsInputModeAlternateMenuIconFileKey + rime.imageset tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -86,14 +92,17 @@ im.rime.inputmethod.Squirrel.Cant + TISIconLabels + + Primary + + TISInputSourceID im.rime.inputmethod.Squirrel.Cant TISIntendedLanguage yue-Hant TISIconIsTemplate - tsInputModeAlternateMenuIconFileKey - rime.svg tsInputModeCharacterRepertoireKey Hant @@ -106,9 +115,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.svg - tsInputModePaletteIconFileKey - rime.svg + rime.imageset + tsInputModeAlternateMenuIconFileKey + rime.imageset tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -142,20 +151,15 @@ https://rime.github.io/release/squirrel/appcast.xml SUPublicDSAKeyFile dsa_pub.pem + NSSupportsSuddenTermination + TICapsLockLanguageSwitchCapable - tsInputMethodCharacterRepertoireKey - - Hant - Hans - TISIconIsTemplate tsInputMethodIconFileKey - rime.svg - tsInputMethodAlternateIconFileKey - rime.svg - tsInputMethodPaletteIconFileKey - rime.svg + rime.imageset + tsInputMethodAlternateMenuIconFileKey + rime.imageset diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 3c25039f5..6e2d3a14f 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -83,8 +83,6 @@ A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; D26434552706A15100857391 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D26434542706A15100857391 /* QuartzCore.framework */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; - F4E409FC2A96C25D00A4C391 /* rime.pdf in Resources */ = {isa = PBXBuildFile; fileRef = F4E409FB2A96C25D00A4C391 /* rime.pdf */; }; - F4E7E0D12A9B19770040701F /* rime.svg in Resources */ = {isa = PBXBuildFile; fileRef = F4E7E0D02A9B19770040701F /* rime.svg */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -281,8 +279,6 @@ F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; - F4E409FB2A96C25D00A4C391 /* rime.pdf */ = {isa = PBXFileReference; explicitFileType = image.icns; path = rime.pdf; sourceTree = ""; }; - F4E7E0D02A9B19770040701F /* rime.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = rime.svg; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -309,8 +305,8 @@ 44AC95181430CF6000C888FB /* SquirrelInputController.h */, 44AC95191430CF6000C888FB /* SquirrelInputController.m */, A44571AB0DBF42C200F793F9 /* macos_keycode.h */, - A47C48DE105E8CE8006D528B /* macos_keycode.m */, 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, + A47C48DE105E8CE8006D528B /* macos_keycode.m */, 4443A8391828CC5100731305 /* input_source.m */, 29B97316FDCFA39411CA2CEA /* main.m */, 44F84AD514E94C490005D70B /* SquirrelPanel.h */, @@ -368,8 +364,6 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( - F4E7E0D02A9B19770040701F /* rime.svg */, - F4E409FB2A96C25D00A4C391 /* rime.pdf */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, 44F7708E152B3334005CF491 /* dsa_pub.pem */, @@ -542,12 +536,10 @@ buildActionMask = 2147483647; files = ( 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, - F4E7E0D12A9B19770040701F /* rime.svg in Resources */, A45578F51146A75200592C6E /* MainMenu.xib in Resources */, 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, - F4E409FC2A96C25D00A4C391 /* rime.pdf in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, ); @@ -620,7 +612,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.17.2; + CURRENT_PROJECT_VERSION = 0.16.3; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -674,7 +666,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 0.17.2; + CURRENT_PROJECT_VERSION = 0.16.3; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; FRAMEWORK_SEARCH_PATHS = ( @@ -771,7 +763,7 @@ LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; + SDKROOT = macosx14.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; }; name = Debug; @@ -827,7 +819,7 @@ LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = NO; - SDKROOT = macosx; + SDKROOT = macosx14.0; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; }; name = Release; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 6e8a055c0..e937dd7b6 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -124,7 +124,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender break; } [self rimeUpdate]; - saveStatus: _lastEventCount = eventCount; + saveStatus: _lastEventCount = eventCount; } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. @@ -183,8 +183,7 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (!handled) { BOOL isVimBackInCommandMode = rime_keycode == XK_Escape || ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || - rime_keycode == XK_C || - rime_keycode == XK_bracketleft)); + rime_keycode == XK_C || rime_keycode == XK_bracketleft)); if (isVimBackInCommandMode && rime_get_api()->get_option(_session, "vim_mode") && !rime_get_api()->get_option(_session, "ascii_mode")) { @@ -335,14 +334,14 @@ - (void)deactivateServer:(id)sender } /*! - @method - @abstract Called when a user action was taken that ends an input session. - Typically triggered by the user selecting a new input method - or keyboard layout. - @discussion When this method is called your controller should send the - current input buffer to the client via a call to - insertText:replacementRange:. Additionally, this is the time - to clean up if that is necessary. + @method + @abstract Called when a user action was taken that ends an input session. + Typically triggered by the user selecting a new input method + or keyboard layout. + @discussion When this method is called your controller should send the + current input buffer to the client via a call to + insertText:replacementRange:. Additionally, this is the time + to clean up if that is necessary. */ - (void)commitComposition:(id)sender @@ -472,14 +471,9 @@ - (void)showPreeditString:(NSString *)preedit [_preeditString addAttributes:attrs range:convertedRange]; } if (range.location < pos) { - attrs = [self markForStyle:kTSMHiliteSelectedConvertedText atRange:range]; + attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:range]; [_preeditString addAttributes:attrs range:range]; } - if (MIN(NSMaxRange(range), pos) < preedit.length) { - NSRange rawRange = NSMakeRange(MIN(NSMaxRange(range), pos), preedit.length - MIN(NSMaxRange(range), pos)); - attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; - [_preeditString addAttributes:attrs range:rawRange]; - } [self updateComposition]; } diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 9c752b3df..fbcc9b38b 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -43,7 +43,7 @@ - (CGPathRef)quartzPath { break; } } - // Be sure the path is closed or Quartz may not do valid hit detection. + // Be sure the path is closed or Quartz may not do valid hit detection. if (!didClosePath) { CGPathCloseSubpath(path); } @@ -53,6 +53,7 @@ - (CGPathRef)quartzPath { return immutablePath; } } + @end static const CGFloat kOffsetHeight = 5; @@ -312,12 +313,12 @@ - (void) setAttrs:(NSMutableDictionary *)attrs _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; - symbolAttrsForwardFill[NSGlyphInfoAttributeName] = + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶\uFE0E"]; _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶\uFE0E" attributes:symbolAttrsForwardFill]; NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; - symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷\uFE0E"]; _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷\uFE0E" attributes:symbolAttrsForwardStroke]; } @@ -486,7 +487,8 @@ - (NSRect)contentRectForRange:(NSRange)range { return YES; }]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName - atIndex:NSMaxRange(range) - 1 effectiveRange:NULL] lineSpacing]; + atIndex:NSMaxRange(range) - 1 + effectiveRange:NULL] lineSpacing]; contentRect.size.height += lineSpacing; return contentRect; } else { @@ -839,113 +841,113 @@ - (void)updateLayer { candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.hilitedCornerRadius); // Draw candidate highlight rect - if (theme.linear) { - CGFloat gridOriginY = NSMinY(candidateBlockRect); - CGFloat tabInterval = theme.separatorWidth * 2; - if (theme.tabled) { - candidateHorzGridPath = [NSBezierPath bezierPath]; - candidateVertGridPath = [NSBezierPath bezierPath]; - } - for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); - if (candidateRange.length == 0) { - break; - } - NSRect leadingRect = NSZeroRect; - NSRect bodyRect = NSZeroRect; - NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - if (preeditRange.length == 0) { - leadingRect.origin.y += theme.linespace / 2; - bodyRect.origin.y += theme.linespace / 2; - trailingRect.origin.y += theme.linespace / 2; - } - if (!NSIsEmptyRect(leadingRect)) { - leadingRect.origin.y -= theme.linespace / 2; - leadingRect.size.height += theme.linespace / 2; - leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); - } - if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.height += theme.linespace / 2; - trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); + if (theme.linear) { + CGFloat gridOriginY = NSMinY(candidateBlockRect); + CGFloat tabInterval = theme.separatorWidth * 2; + if (theme.tabled) { + candidateHorzGridPath = [NSBezierPath bezierPath]; + candidateVertGridPath = [NSBezierPath bezierPath]; } - if (!NSIsEmptyRect(bodyRect)) { - if (NSIsEmptyRect(leadingRect)) { - bodyRect.origin.y -= theme.linespace / 2; - bodyRect.size.height += theme.linespace / 2; + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { + NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + if (candidateRange.length == 0) { + break; } - if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.height += theme.linespace / 2; + NSRect leadingRect = NSZeroRect; + NSRect bodyRect = NSZeroRect; + NSRect trailingRect = NSZeroRect; + [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect + : NSInsetRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); + if (preeditRange.length == 0) { + leadingRect.origin.y += theme.linespace / 2; + bodyRect.origin.y += theme.linespace / 2; + trailingRect.origin.y += theme.linespace / 2; } - bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); - } - if (theme.tabled) { - CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border - [candidateHorzGridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth / 2, bottomEdge)]; - [candidateHorzGridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth / 2, bottomEdge)]; - [candidateHorzGridPath closePath]; - gridOriginY = bottomEdge; + if (!NSIsEmptyRect(leadingRect)) { + leadingRect.origin.y -= theme.linespace / 2; + leadingRect.size.height += theme.linespace / 2; + leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); } - CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; - if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar - [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.hilitedCornerRadius)]; - [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.hilitedCornerRadius)]; - [candidateVertGridPath closePath]; + if (!NSIsEmptyRect(trailingRect)) { + trailingRect.size.height += theme.linespace / 2; + trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); } - CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((tailEdge + theme.separatorWidth / 2 - _insets.left) / tabInterval) * tabInterval + _insets.left; - if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; - } else if (NSIsEmptyRect(bodyRect)) { - trailingRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + if (!NSIsEmptyRect(bodyRect)) { + if (NSIsEmptyRect(leadingRect)) { + bodyRect.origin.y -= theme.linespace / 2; + bodyRect.size.height += theme.linespace / 2; + } + if (NSIsEmptyRect(trailingRect)) { + bodyRect.size.height += theme.linespace / 2; + } + bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); + } + if (theme.tabled) { + CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border + [candidateHorzGridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth / 2, bottomEdge)]; + [candidateHorzGridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth / 2, bottomEdge)]; + [candidateHorzGridPath closePath]; + gridOriginY = bottomEdge; + } + CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; + if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar + [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.hilitedCornerRadius)]; + [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.hilitedCornerRadius)]; + [candidateVertGridPath closePath]; + } + CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + CGFloat tabPosition = ceil((tailEdge + theme.separatorWidth / 2 - _insets.left) / tabInterval) * tabInterval + _insets.left; + if (NSIsEmptyRect(trailingRect)) { + bodyRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + } else if (NSIsEmptyRect(bodyRect)) { + trailingRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + } else { + bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); + trailingRect = NSZeroRect; + } + } + NSArray *candidatePoints; + NSArray *candidatePoints2; + // Handles the special case where containing boxes are separated + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && + !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + candidatePoints = rectVertex(leadingRect); + candidatePoints2 = rectVertex(trailingRect); } else { - bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); - trailingRect = NSZeroRect; + candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + if (candidatePoints2.count > 0) { + [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; + } + _candidatePaths[i] = candidatePath; } - NSArray *candidatePoints; - NSArray *candidatePoints2; - // Handles the special case where containing boxes are separated - if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && - !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePoints = rectVertex(leadingRect); - candidatePoints2 = rectVertex(trailingRect); - } else { - candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); - } - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); - if (candidatePoints2.count > 0) { - [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; - } - _candidatePaths[i] = candidatePath; - } - } else { // stacked layout - for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); - if (candidateRange.length == 0) { - break; - } - NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); - candidateRect.size.width = textContainerRect.size.width; - candidateRect.origin.x = textContainerRect.origin.x; - candidateRect.origin.y += textContainerRect.origin.y; - if (preeditRange.length == 0) { - candidateRect.origin.y += theme.linespace / 2; + } else { // stacked layout + for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { + NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + if (candidateRange.length == 0) { + break; + } + NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); + candidateRect.size.width = textContainerRect.size.width; + candidateRect.origin.x = textContainerRect.origin.x; + candidateRect.origin.y += textContainerRect.origin.y; + if (preeditRange.length == 0) { + candidateRect.origin.y += theme.linespace / 2; + } + candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); + NSArray *candidatePoints = rectVertex(candidateRect); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + _candidatePaths[i] = candidatePath; } - candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); - NSArray *candidatePoints = rectVertex(candidateRect); - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); - _candidatePaths[i] = candidatePath; } } -} // Draw paging Rect if (pagingRange.length > 0) { @@ -2106,7 +2108,7 @@ - (void)showPreedit:(NSString *)preedit // text done! [self setAnimationBehavior:caretPos == NSNotFound ? - NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; + NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; [_view drawViewWithInsets:insets candidateRanges:candidateRanges highlightedIndex:index @@ -2478,11 +2480,13 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) size:fontSize]; - NSDictionary *monoDigitAttrs = @{NSFontFeatureSettingsAttribute: - @[@{NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector)}, - @{NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector)}]}; + NSDictionary *monoDigitAttrs = @{ + NSFontFeatureSettingsAttribute: + @[@{ NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector) }, + @{ NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector) }] + }; NSFontDescriptor *labelFontDescriptor = [(getFontDescriptor(labelFontName) ? : fontDescriptor) fontDescriptorByAddingAttributes:monoDigitAttrs]; NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] diff --git a/rime.pdf b/rime.pdf deleted file mode 100644 index 7145edc7406a705cb04280df7c945cf82128dde6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51874 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 Xj!p&+PX8D&&i9%0mt!fRD2e(Xp&ZG7 diff --git a/rime.svg b/rime.svg deleted file mode 100644 index 1ac3677d1..000000000 --- a/rime.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - From 84287ad08095d69d751f100b91a7b4b7228a63ea Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 29 Sep 2023 00:08:58 +0200 Subject: [PATCH 141/164] make file --- Assets.xcassets/rime.imageset/Contents.json | 2 +- INSTALL.md | 8 +- Makefile | 7 +- Squirrel.xcodeproj/project.pbxproj | 96 +++++++++++++++++---- autobuild.sh | 20 ++--- librime | 2 +- 6 files changed, 95 insertions(+), 40 deletions(-) diff --git a/Assets.xcassets/rime.imageset/Contents.json b/Assets.xcassets/rime.imageset/Contents.json index 7a6710a1c..7bb660934 100644 --- a/Assets.xcassets/rime.imageset/Contents.json +++ b/Assets.xcassets/rime.imageset/Contents.json @@ -2,7 +2,7 @@ "images" : [ { "filename" : "rime.svg", - "idiom" : "mac" + "idiom" : "universal" } ], "info" : { diff --git a/INSTALL.md b/INSTALL.md index 07d6d2354..749d1f605 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -59,7 +59,7 @@ Choose one of the following options. ``` sh export BUILD_UNIVERSAL=1 -make -C librime xcode/deps/boost +bash librime/install-boost.sh export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" ``` @@ -99,7 +99,9 @@ Build librime, dependent third-party libraries and data files: ``` sh export BUILD_UNIVERSAL=1 +export CMAKE_GENERATOR=Ninja +make -C librime make deps ``` @@ -115,10 +117,10 @@ To build only for the native architecture, pass variable `ARCHS` to `make`: ``` sh # for Mac computers with Apple Silicon -make ARCHS='arm64' MACOSX_DEPLOYMENT_TARGET='10.14' +make ARCHS='arm64' MACOSX_DEPLOYMENT_TARGET='10.15' # for Intel-based Mac -make ARCHS='x86_64' MACOSX_DEPLOYMENT_TARGET='10.14' +make ARCHS='x86_64' MACOSX_DEPLOYMENT_TARGET='10.15' ``` ## Install it on your Mac diff --git a/Makefile b/Makefile index 7db9bf8da..cbe9d4d01 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ all: release install: install-release # Change to `dist-with-icu` if boost is linked to icu libraries. -RIME_DIST_TARGET = dist +RIME_DIST_TARGET = install RIME_BIN_DIR = librime/dist/bin RIME_LIB_DIR = librime/dist/lib @@ -85,12 +85,11 @@ ifdef ARCHS BUILD_SETTINGS += ARCHS="$(ARCHS)" BUILD_SETTINGS += ONLY_ACTIVE_ARCH=NO _=$() $() -export CMAKE_OSX_ARCHITECTURES = $(subst $(_),;,$(ARCHS)) +export CMAKE_OSX_ARCHITECTURES = $(subst $(_),",",$(ARCHS)) endif -ifdef MACOSX_DEPLOYMENT_TARGET +MACOSX_DEPLOYMENT_TARGET ?= 10.15 BUILD_SETTINGS += MACOSX_DEPLOYMENT_TARGET="$(MACOSX_DEPLOYMENT_TARGET)" -endif release: $(DEPS_CHECK) bash package/add_data_files diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 6e2d3a14f..095d9e9f3 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -48,7 +48,6 @@ 77AA681D2588916F00A592E2 /* s2hk.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F02588916300A592E2 /* s2hk.json */; }; 77AA681E2588916F00A592E2 /* s2t.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67ED2588916300A592E2 /* s2t.json */; }; 77AA681F2588916F00A592E2 /* s2tw.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F52588916400A592E2 /* s2tw.json */; }; - 77AA68202588916F00A592E2 /* s2twp.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67EA2588916300A592E2 /* s2twp.json */; }; 77AA68212588916F00A592E2 /* STCharacters.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F32588916400A592E2 /* STCharacters.ocd2 */; }; 77AA68222588916F00A592E2 /* STPhrases.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F72588916400A592E2 /* STPhrases.ocd2 */; }; 77AA68232588916F00A592E2 /* t2hk.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67E82588916300A592E2 /* t2hk.json */; }; @@ -58,9 +57,6 @@ 77AA68272588916F00A592E2 /* TSCharacters.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67E32588916300A592E2 /* TSCharacters.ocd2 */; }; 77AA68282588916F00A592E2 /* TSPhrases.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F62588916400A592E2 /* TSPhrases.ocd2 */; }; 77AA68292588916F00A592E2 /* tw2s.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67DF2588916300A592E2 /* tw2s.json */; }; - 77AA682A2588916F00A592E2 /* tw2sp.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67EC2588916300A592E2 /* tw2sp.json */; }; - 77AA682B2588916F00A592E2 /* TWPhrases.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67E72588916300A592E2 /* TWPhrases.ocd2 */; }; - 77AA682C2588916F00A592E2 /* TWPhrasesRev.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67EB2588916300A592E2 /* TWPhrasesRev.ocd2 */; }; 77AA682D2588916F00A592E2 /* TWVariants.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F12588916300A592E2 /* TWVariants.ocd2 */; }; 77AA682E2588916F00A592E2 /* TWVariantsRev.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F42588916400A592E2 /* TWVariantsRev.ocd2 */; }; 77AA682F2588916F00A592E2 /* TWVariantsRevPhrases.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = 77AA67F22588916300A592E2 /* TWVariantsRevPhrases.ocd2 */; }; @@ -83,6 +79,25 @@ A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; D26434552706A15100857391 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D26434542706A15100857391 /* QuartzCore.framework */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; + F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */; }; + F4E457BA2AD97412003F6D79 /* t2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457A92AD97411003F6D79 /* t2emoji.json */; }; + F4E457BB2AD97412003F6D79 /* hk2t.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AA2AD97411003F6D79 /* hk2t.json */; }; + F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AB2AD97411003F6D79 /* EmojiWordEN.ocd2 */; }; + F4E457BD2AD97412003F6D79 /* s2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AC2AD97412003F6D79 /* s2emoji.json */; }; + F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AD2AD97412003F6D79 /* EmojiCategoryHK.ocd2 */; }; + F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AE2AD97412003F6D79 /* EmojiWordT.ocd2 */; }; + F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AF2AD97412003F6D79 /* EmojiCategoryEN.ocd2 */; }; + F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B02AD97412003F6D79 /* EmojiWordTW.ocd2 */; }; + F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B12AD97412003F6D79 /* EmojiWordHK.ocd2 */; }; + F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B22AD97412003F6D79 /* EmojiWordS.ocd2 */; }; + F4E457C42AD97412003F6D79 /* en2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B32AD97412003F6D79 /* en2emoji.json */; }; + F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B42AD97412003F6D79 /* EmojiCategoryS.ocd2 */; }; + F4E457C62AD97412003F6D79 /* tw2t.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B52AD97412003F6D79 /* tw2t.json */; }; + F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */; }; + F4E457C82AD97412003F6D79 /* hk2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B72AD97412003F6D79 /* hk2emoji.json */; }; + F4E457C92AD97412003F6D79 /* tw2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B82AD97412003F6D79 /* tw2emoji.json */; }; + F4E457CC2AD97499003F6D79 /* TWPhrases.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */; }; + F4E457CD2AD97499003F6D79 /* TWPhrasesRev.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -104,7 +119,6 @@ 77AA681D2588916F00A592E2 /* s2hk.json in Copy opencc Files */, 77AA681E2588916F00A592E2 /* s2t.json in Copy opencc Files */, 77AA681F2588916F00A592E2 /* s2tw.json in Copy opencc Files */, - 77AA68202588916F00A592E2 /* s2twp.json in Copy opencc Files */, 77AA68212588916F00A592E2 /* STCharacters.ocd2 in Copy opencc Files */, 77AA68222588916F00A592E2 /* STPhrases.ocd2 in Copy opencc Files */, 77AA68232588916F00A592E2 /* t2hk.json in Copy opencc Files */, @@ -114,9 +128,6 @@ 77AA68272588916F00A592E2 /* TSCharacters.ocd2 in Copy opencc Files */, 77AA68282588916F00A592E2 /* TSPhrases.ocd2 in Copy opencc Files */, 77AA68292588916F00A592E2 /* tw2s.json in Copy opencc Files */, - 77AA682A2588916F00A592E2 /* tw2sp.json in Copy opencc Files */, - 77AA682B2588916F00A592E2 /* TWPhrases.ocd2 in Copy opencc Files */, - 77AA682C2588916F00A592E2 /* TWPhrasesRev.ocd2 in Copy opencc Files */, 77AA682D2588916F00A592E2 /* TWVariants.ocd2 in Copy opencc Files */, 77AA682E2588916F00A592E2 /* TWVariantsRev.ocd2 in Copy opencc Files */, 77AA682F2588916F00A592E2 /* TWVariantsRevPhrases.ocd2 in Copy opencc Files */, @@ -241,12 +252,8 @@ 77AA67E42588916300A592E2 /* JPVariantsRev.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = JPVariantsRev.ocd2; sourceTree = ""; }; 77AA67E52588916300A592E2 /* JPShinjitaiCharacters.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = JPShinjitaiCharacters.ocd2; sourceTree = ""; }; 77AA67E62588916300A592E2 /* JPVariants.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = JPVariants.ocd2; sourceTree = ""; }; - 77AA67E72588916300A592E2 /* TWPhrases.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrases.ocd2; sourceTree = ""; }; 77AA67E82588916300A592E2 /* t2hk.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = t2hk.json; sourceTree = ""; }; 77AA67E92588916300A592E2 /* HKVariantsRevPhrases.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = HKVariantsRevPhrases.ocd2; sourceTree = ""; }; - 77AA67EA2588916300A592E2 /* s2twp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = s2twp.json; sourceTree = ""; }; - 77AA67EB2588916300A592E2 /* TWPhrasesRev.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrasesRev.ocd2; sourceTree = ""; }; - 77AA67EC2588916300A592E2 /* tw2sp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tw2sp.json; sourceTree = ""; }; 77AA67ED2588916300A592E2 /* s2t.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = s2t.json; sourceTree = ""; }; 77AA67EE2588916300A592E2 /* jp2t.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = jp2t.json; sourceTree = ""; }; 77AA67EF2588916300A592E2 /* t2jp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = t2jp.json; sourceTree = ""; }; @@ -279,6 +286,25 @@ F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; + F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryTW.ocd2; sourceTree = ""; }; + F4E457A92AD97411003F6D79 /* t2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = t2emoji.json; sourceTree = ""; }; + F4E457AA2AD97411003F6D79 /* hk2t.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hk2t.json; sourceTree = ""; }; + F4E457AB2AD97411003F6D79 /* EmojiWordEN.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiWordEN.ocd2; sourceTree = ""; }; + F4E457AC2AD97412003F6D79 /* s2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = s2emoji.json; sourceTree = ""; }; + F4E457AD2AD97412003F6D79 /* EmojiCategoryHK.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryHK.ocd2; sourceTree = ""; }; + F4E457AE2AD97412003F6D79 /* EmojiWordT.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiWordT.ocd2; sourceTree = ""; }; + F4E457AF2AD97412003F6D79 /* EmojiCategoryEN.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryEN.ocd2; sourceTree = ""; }; + F4E457B02AD97412003F6D79 /* EmojiWordTW.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiWordTW.ocd2; sourceTree = ""; }; + F4E457B12AD97412003F6D79 /* EmojiWordHK.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiWordHK.ocd2; sourceTree = ""; }; + F4E457B22AD97412003F6D79 /* EmojiWordS.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiWordS.ocd2; sourceTree = ""; }; + F4E457B32AD97412003F6D79 /* en2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = en2emoji.json; sourceTree = ""; }; + F4E457B42AD97412003F6D79 /* EmojiCategoryS.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryS.ocd2; sourceTree = ""; }; + F4E457B52AD97412003F6D79 /* tw2t.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tw2t.json; sourceTree = ""; }; + F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryT.ocd2; sourceTree = ""; }; + F4E457B72AD97412003F6D79 /* hk2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hk2emoji.json; sourceTree = ""; }; + F4E457B82AD97412003F6D79 /* tw2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tw2emoji.json; sourceTree = ""; }; + F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrases.ocd2; sourceTree = ""; }; + F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrasesRev.ocd2; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -389,6 +415,23 @@ 4407F39D14EC071E001329FE /* opencc */ = { isa = PBXGroup; children = ( + F4E457AF2AD97412003F6D79 /* EmojiCategoryEN.ocd2 */, + F4E457AD2AD97412003F6D79 /* EmojiCategoryHK.ocd2 */, + F4E457B42AD97412003F6D79 /* EmojiCategoryS.ocd2 */, + F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */, + F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */, + F4E457AB2AD97411003F6D79 /* EmojiWordEN.ocd2 */, + F4E457B12AD97412003F6D79 /* EmojiWordHK.ocd2 */, + F4E457B22AD97412003F6D79 /* EmojiWordS.ocd2 */, + F4E457AE2AD97412003F6D79 /* EmojiWordT.ocd2 */, + F4E457B02AD97412003F6D79 /* EmojiWordTW.ocd2 */, + F4E457B32AD97412003F6D79 /* en2emoji.json */, + F4E457B72AD97412003F6D79 /* hk2emoji.json */, + F4E457AA2AD97411003F6D79 /* hk2t.json */, + F4E457AC2AD97412003F6D79 /* s2emoji.json */, + F4E457A92AD97411003F6D79 /* t2emoji.json */, + F4E457B82AD97412003F6D79 /* tw2emoji.json */, + F4E457B52AD97412003F6D79 /* tw2t.json */, 77AA67E22588916300A592E2 /* hk2s.json */, 77AA67DC2588916300A592E2 /* HKVariants.ocd2 */, 77AA67E02588916300A592E2 /* HKVariantsRev.ocd2 */, @@ -401,7 +444,6 @@ 77AA67F02588916300A592E2 /* s2hk.json */, 77AA67ED2588916300A592E2 /* s2t.json */, 77AA67F52588916400A592E2 /* s2tw.json */, - 77AA67EA2588916300A592E2 /* s2twp.json */, 77AA67F32588916400A592E2 /* STCharacters.ocd2 */, 77AA67F72588916400A592E2 /* STPhrases.ocd2 */, 77AA67E82588916300A592E2 /* t2hk.json */, @@ -410,10 +452,9 @@ 77AA67DE2588916300A592E2 /* t2tw.json */, 77AA67E32588916300A592E2 /* TSCharacters.ocd2 */, 77AA67F62588916400A592E2 /* TSPhrases.ocd2 */, + F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */, + F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */, 77AA67DF2588916300A592E2 /* tw2s.json */, - 77AA67EC2588916300A592E2 /* tw2sp.json */, - 77AA67E72588916300A592E2 /* TWPhrases.ocd2 */, - 77AA67EB2588916300A592E2 /* TWPhrasesRev.ocd2 */, 77AA67F12588916300A592E2 /* TWVariants.ocd2 */, 77AA67F42588916400A592E2 /* TWVariantsRev.ocd2 */, 77AA67F22588916300A592E2 /* TWVariantsRevPhrases.ocd2 */, @@ -535,13 +576,32 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + F4E457BB2AD97412003F6D79 /* hk2t.json in Resources */, + F4E457C42AD97412003F6D79 /* en2emoji.json in Resources */, + F4E457CC2AD97499003F6D79 /* TWPhrases.ocd2 in Resources */, + F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Resources */, 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + F4E457BA2AD97412003F6D79 /* t2emoji.json in Resources */, A45578F51146A75200592C6E /* MainMenu.xib in Resources */, + F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Resources */, + F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Resources */, 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, + F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, + F4E457C62AD97412003F6D79 /* tw2t.json in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, + F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Resources */, + F4E457C92AD97412003F6D79 /* tw2emoji.json in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, + F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Resources */, + F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Resources */, + F4E457C82AD97412003F6D79 /* hk2emoji.json in Resources */, + F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Resources */, + F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Resources */, + F4E457BD2AD97412003F6D79 /* s2emoji.json in Resources */, + F4E457CD2AD97499003F6D79 /* TWPhrasesRev.ocd2 in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, + F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -763,7 +823,7 @@ LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx14.0; + SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; }; name = Debug; @@ -819,7 +879,7 @@ LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; ONLY_ACTIVE_ARCH = NO; - SDKROOT = macosx14.0; + SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; }; name = Release; diff --git a/autobuild.sh b/autobuild.sh index 46fd396de..9691c4f9e 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -1,23 +1,17 @@ make clean clean-deps -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua +# git submodule update --init --recursive -git submodules update --init --recursive +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua export BUILD_UNIVERSAL=1 - +bash librime/install-boost.sh export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" export CMAKE_GENERATOR=Ninja - -bash librime/install-boost.sh - -make librime deps - -make librime install +export PATH="/opt/homebrew/opt/llvm/bin:/usr/local/opt/llvm/bin:$PATH" +make -C librime ARCHS="arm64;x86_64" test +make -C librime ARCHS="arm64;x86_64" install make deps - -make - -make install \ No newline at end of file +make ARCHS="arm64;x86_64" install diff --git a/librime b/librime index a9abcfe07..8d842922e 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit a9abcfe078d0d198252d35dc5683de978f304e13 +Subproject commit 8d842922e5dcc0c4dcfd492696ec6ab1c4c5e2c1 From 969a9010c3c095ac46c290d0aba82fcdbab27387 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 3 Oct 2023 07:00:09 +0200 Subject: [PATCH 142/164] say ZH voices --- en.lproj/Localizable.strings | 5 +++++ main.m | 10 ++++------ zh-HK.lproj/Localizable.strings | 5 +++++ zh-Hans.lproj/Localizable.strings | 5 +++++ zh-Hant.lproj/Localizable.strings | 5 +++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 02184186b..f18edf7fd 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -4,3 +4,8 @@ "deploy_start" = "Deploying Rime input method engine…"; "deploy_success" = "Squirrel is ready."; "deploy_failure" = "Error occurred. See log file $TMPDIR/rime.squirrel.INFO."; + +"problematic_launch" = "Problematic launch detected! \ + Squirrel may be suffering a crash due to imporper configuration. \ + Revert previous modifications to see if the problem recurs."; +"say_voice" = "Alex"; diff --git a/main.m b/main.m index 8feae1ce3..bc6887748 100644 --- a/main.m +++ b/main.m @@ -78,12 +78,10 @@ int main(int argc, char *argv[]) { if (NSApp.squirrelAppDelegate.problematicLaunchDetected) { NSLog(@"Problematic launch detected!"); - NSArray *args = @[ - @"Problematic launch detected! \ - Squirrel may be suffering a crash due to imporper configuration. \ - Revert previous modifications to see if the problem recurs." - ]; - [NSTask launchedTaskWithLaunchPath:@"/usr/bin/say" arguments:args]; + NSArray *args = @[@"-v", NSLocalizedString(@"say_voice", nil), + NSLocalizedString(@"problematic_launch", nil)]; + [NSTask launchedTaskWithExecutableURL:[[NSURL alloc] initFileURLWithPath:@"/usr/bin/say"] + arguments:args error:nil terminationHandler:nil]; } else { [NSApp.squirrelAppDelegate setupRime]; [NSApp.squirrelAppDelegate startRimeWithFullCheck:NO]; diff --git a/zh-HK.lproj/Localizable.strings b/zh-HK.lproj/Localizable.strings index 47f3756d1..04c993571 100644 --- a/zh-HK.lproj/Localizable.strings +++ b/zh-HK.lproj/Localizable.strings @@ -4,3 +4,8 @@ "deploy_start" = "部署輸入法引擎⋯"; "deploy_success" = "部署完成。"; "deploy_failure" = "有錯誤!請查看日誌 $TMPDIR/rime.squirrel.INFO"; + +"problematic_launch" = "啟動時偵測到錯誤!\ + 「鼠鬚筆」可能由於設定不當而崩潰。\ + 請嘗試回退先前的改動,然後查看問題是否仍然存在。"; +"say_voice" = "Sinji"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 1e49ceda3..e6d965acb 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -4,3 +4,8 @@ "deploy_start" = "部署输入法引擎…"; "deploy_success" = "部署完成。"; "deploy_failure" = "有错误!请查看日志 $TMPDIR/rime.squirrel.INFO"; + +"problematic_launch" = "检测到启动有问题!\ + “鼠须管”可能因错误设置而崩溃。\ + 请尝试撤销之前的修改,然后查看问题是否仍旧存在。"; +"say_voice" = "TingTing"; diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index 3880098db..0cb0284f3 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -4,3 +4,8 @@ "deploy_start" = "部署輸入法引擎⋯"; "deploy_success" = "部署完成。"; "deploy_failure" = "有錯誤!請查看日誌 $TMPDIR/rime.squirrel.INFO"; + +"problematic_launch" = "啟動時偵測到問題!\ + 「鼠鬚管」可能因設定不當而崩潰。\ + 請嘗試回退先前的修改,然後查看問題是否依然存在。"; +"say_voice" = "MeiJia"; From 4bddc4fe95fdcc32b3b85dce144b7d007dae1a0e Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 27 Sep 2023 23:56:56 +0200 Subject: [PATCH 143/164] style override by switches --- Squirrel.xcodeproj/project.pbxproj | 2 +- SquirrelApplicationDelegate.h | 2 + SquirrelApplicationDelegate.m | 54 +- SquirrelConfig.h | 30 +- SquirrelConfig.m | 141 +++- SquirrelInputController.h | 2 + SquirrelInputController.m | 74 +- SquirrelPanel.h | 6 +- SquirrelPanel.m | 1200 ++++++++++++++-------------- main.m | 8 +- 10 files changed, 875 insertions(+), 644 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 095d9e9f3..d2c7d93e9 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -331,8 +331,8 @@ 44AC95181430CF6000C888FB /* SquirrelInputController.h */, 44AC95191430CF6000C888FB /* SquirrelInputController.m */, A44571AB0DBF42C200F793F9 /* macos_keycode.h */, - 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, A47C48DE105E8CE8006D528B /* macos_keycode.m */, + 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */, 4443A8391828CC5100731305 /* input_source.m */, 29B97316FDCFA39411CA2CEA /* main.m */, 44F84AD514E94C490005D70B /* SquirrelPanel.h */, diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index 783811dff..a4bbc061e 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -2,6 +2,7 @@ @class SquirrelConfig; @class SquirrelPanel; +@class SquirrelOptionSwitcher; // Note: the SquirrelApplicationDelegate is instantiated automatically as an outlet of NSApp's instance @interface SquirrelApplicationDelegate : NSObject @@ -22,6 +23,7 @@ - (void)startRimeWithFullCheck:(BOOL)fullCheck; - (void)loadSettings; - (void)loadSchemaSpecificSettings:(NSString *)schemaId; +- (void)loadSchemaSpecificLabels:(NSString *)schemaId; @property(nonatomic, readonly) BOOL problematicLaunchDetected; diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 79fd5effc..59f5dee6e 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -24,7 +24,7 @@ - (IBAction)syncUserData:(id)sender - (IBAction)configure:(id)sender { - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[@"file://" stringByAppendingString:(@"~/Library/Rime").stringByStandardizingPath]]]; + [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:[@"~/Library/Rime/" stringByExpandingTildeInPath]]]; } - (IBAction)openWiki:(id)sender @@ -69,13 +69,10 @@ void notification_handler(void *context_object, RimeSessionId session_id, } return; } - // off? id app_delegate = (__bridge id)context_object; - if (app_delegate && ![app_delegate enableNotifications]) { - return; - } // schema change - if (!strcmp(message_type, "schema")) { + if (!strcmp(message_type, "schema") && + app_delegate && [app_delegate enableNotifications]) { const char *schema_name = strchr(message_value, '/'); if (schema_name) { ++schema_name; @@ -84,14 +81,21 @@ void notification_handler(void *context_object, RimeSessionId session_id, return; } // option change - if (!strcmp(message_type, "option")) { + if (!strcmp(message_type, "option") && app_delegate) { Bool state = message_value[0] != '!'; const char *option_name = message_value + !state; - struct rime_string_slice_t state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); - struct rime_string_slice_t state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); - if (state_label_long.str || state_label_short.str) { - const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; - show_status_message(state_label_long.str, short_message, message_type); + if ([[app_delegate panel].optionSwitcher containsOption:@(option_name)]) { + if ([[app_delegate panel].optionSwitcher updateGroupState:@(message_value) ofOption:@(option_name)]) { + [app_delegate loadSchemaSpecificSettings:[app_delegate panel].optionSwitcher.schemaId]; + } + } + if ([app_delegate enableNotifications]) { + RimeStringSlice state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); + RimeStringSlice state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); + if (state_label_long.str || state_label_short.str) { + const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; + show_status_message(state_label_long.str, short_message, message_type); + } } } } @@ -172,26 +176,40 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { [schema close]; } +- (void)loadSchemaSpecificLabels:(NSString *)schemaId { + if (schemaId.length == 0 || [schemaId characterAtIndex:0] == '.') { + return; + } + SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; + [defaultConfig openWithConfigId:@"default"]; + SquirrelConfig *schema = [[SquirrelConfig alloc] init]; + if ([schema openWithSchemaId:schemaId baseConfig:defaultConfig] && + [schema hasSection:@"menu"]) { + [self.panel loadLabelConfig:schema]; + } else { + [self.panel loadLabelConfig:defaultConfig]; + } + [schema close]; + [defaultConfig close]; +} + // prevent freezing the system - (BOOL)problematicLaunchDetected { BOOL detected = NO; - NSString *logfile = - [NSTemporaryDirectory() stringByAppendingPathComponent:@"squirrel_launch.dat"]; + NSString *logfile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"squirrel_launch.dat"]; //NSLog(@"[DEBUG] archive: %@", logfile); NSData *archive = [NSData dataWithContentsOfFile:logfile options:NSDataReadingUncached error:nil]; if (archive) { - NSDate *previousLaunch; - previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; + NSDate *previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate *now = [NSDate date]; - NSData *record; - record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; + NSData *record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; [record writeToFile:logfile atomically:NO]; return detected; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 2a6f4519d..69b77d98b 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -3,6 +3,29 @@ typedef NSDictionary SquirrelAppOptions; typedef NSMutableDictionary SquirrelMutableAppOptions; +@interface SquirrelOptionSwitcher : NSObject + +@property(nonatomic, strong, readonly) NSString *schemaId; +@property(nonatomic, strong, readonly) NSArray *optionNames; +@property(nonatomic, strong, readonly) NSArray *optionStates; +@property(nonatomic, strong, readonly) NSDictionary *> *optionGroups; +@property(nonatomic, strong, readonly) NSDictionary *switcher; + +- (instancetype)initWithSchemaId:(NSString *)schemaId + switcher:(NSDictionary *)switcher + optionGroups:(NSDictionary *> *)optionGroups; + +// return whether switcher options has been successfully updated +- (BOOL)updateSwitcher:(NSDictionary *)switcher; + +- (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName; + +- (BOOL)containsOption:(NSString *)optionName; + +- (NSMutableDictionary *)mutableSwitcher; + +@end + @interface SquirrelConfig : NSObject @property(nonatomic, readonly) BOOL isOpen; @@ -12,12 +35,14 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (BOOL)openBaseConfig; - (BOOL)openWithSchemaId:(NSString *)schemaId baseConfig:(SquirrelConfig *)config; +- (BOOL)openUserConfig:(NSString *)configId; +- (BOOL)openWithConfigId:(NSString *)configId; - (void)close; - (BOOL)hasSection:(NSString *)section; - (BOOL)getBool:(NSString *)option; -- (NSInteger)getInt:(NSString *)option; +- (int)getInt:(NSString *)option; - (double)getDouble:(NSString *)option; - (NSNumber *)getOptionalBool:(NSString *)option; - (NSNumber *)getOptionalInt:(NSString *)option; @@ -29,6 +54,9 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; // file path (absolute or relative to ~/Library/Rime) - (NSColor *)getPattern:(NSString *)option; +- (NSArray *)getList:(NSString *)option; + +- (SquirrelOptionSwitcher *)getOptionSwitcher; - (SquirrelAppOptions *)getAppOptions:(NSString *)appName; @end diff --git a/SquirrelConfig.m b/SquirrelConfig.m index f2c685dc0..3c40058e6 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -2,6 +2,81 @@ #import +@implementation SquirrelOptionSwitcher { + NSString *_schemaId; + NSDictionary *_switcher; + NSDictionary *> *_optionGroups; + NSArray *_optionNames; +} + +- (instancetype)initWithSchemaId:(NSString *)schemaId + switcher:(NSDictionary *)switcher + optionGroups:(NSDictionary *> *)optionGroups{ + self = [super init]; + if (self) { + _schemaId = schemaId; + _switcher = switcher; + _optionGroups = optionGroups; + _optionNames = [switcher allKeys]; + } + return self; +} + +- (NSString *)schemaId { + return _schemaId; +} + +- (NSArray *)optionNames { + return _optionNames; +} + +- (NSArray *)optionStates { + return [_switcher allValues]; +} + +- (NSDictionary *)switcher { + return _switcher; +} + +- (BOOL)updateSwitcher:(NSDictionary *)switcher { + if (switcher.count != _switcher.count) { + return NO; + } + NSMutableDictionary *updatedSwitcher = + [[NSMutableDictionary alloc] initWithCapacity:switcher.count]; + for (NSString *option in _optionNames) { + if (switcher[option] == nil) { + return NO; + } + updatedSwitcher[option] = switcher[option]; + } + _switcher = [updatedSwitcher copy]; + return YES; +} + +- (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName { + NSArray *optionGroup = _optionGroups[optionName]; + if (!optionGroup || ![optionGroup containsObject:optionState]) { + return NO; + } + NSMutableDictionary *updatedSwitcher = [_switcher mutableCopy]; + for (NSString *option in optionGroup) { + updatedSwitcher[option] = optionState; + } + _switcher = [updatedSwitcher copy]; + return YES; +} + +- (BOOL)containsOption:(NSString *)optionName { + return [_optionNames containsObject:optionName]; +} + +- (NSMutableDictionary *)mutableSwitcher { + return [_switcher mutableCopy]; +} + +@end + @implementation SquirrelConfig { NSMutableDictionary *_cache; RimeConfig _config; @@ -44,6 +119,18 @@ - (BOOL)openWithSchemaId:(NSString *)schemaId return _isOpen; } +- (BOOL)openUserConfig:(NSString *)configId { + [self close]; + _isOpen = !!rime_get_api()->user_config_open(configId.UTF8String, &_config); + return _isOpen; +} + +- (BOOL)openWithConfigId:(NSString *)configId { + [self close]; + _isOpen = !!rime_get_api()->config_open(configId.UTF8String, &_config); + return _isOpen; +} + - (void)close { if (_isOpen) { rime_get_api()->config_close(&_config); @@ -67,8 +154,8 @@ - (BOOL)getBool:(NSString *)option { return [self getOptionalBool:option].boolValue; } -- (NSInteger)getInt:(NSString *)option { - return [self getOptionalInt:option].integerValue; +- (int)getInt:(NSString *)option { + return [self getOptionalInt:option].intValue; } - (double)getDouble:(NSString *)option { @@ -150,6 +237,56 @@ - (NSColor *)getPattern:(NSString *)option { return [_baseConfig getPattern:option]; } +- (NSArray *)getList:(NSString *)option { + NSMutableArray *strList = [[NSMutableArray alloc] init]; + RimeConfigIterator iterator; + rime_get_api()->config_begin_list(&iterator, &_config, option.UTF8String); + while (rime_get_api()->config_next(&iterator)) { + [strList addObject:[self getString:@(iterator.path)]]; + } + rime_get_api()->config_end(&iterator); + return strList; +} + +- (SquirrelOptionSwitcher *)getOptionSwitcher { + NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *> *optionGroups = [[NSMutableDictionary alloc] init]; + RimeConfigIterator switchIter; + rime_get_api()->config_begin_list(&switchIter, &_config, "switches"); + while (rime_get_api()->config_next(&switchIter)) { + int reset = [self getInt:[@(switchIter.path) stringByAppendingString:@"/reset"]]; + NSString *name = [self getString:[@(switchIter.path) stringByAppendingString:@"/name"]]; + if (name) { + if ([self hasSection:[@"style/!" stringByAppendingString:name]] || + [self hasSection:[@"style/" stringByAppendingString:name]]) { + switcher[name] = reset ? name : [@"!" stringByAppendingString:name]; + optionGroups[name] = @[name]; + } + } else { + NSMutableArray *optionGroup = [[NSMutableArray alloc] init]; + BOOL hasStyleSection = NO; + RimeConfigIterator optionIter; + rime_get_api()->config_begin_list(&optionIter, &_config, [@(switchIter.path) stringByAppendingString:@"/options"].UTF8String); + while (rime_get_api()->config_next(&optionIter)) { + NSString *option = [self getString:@(optionIter.path)]; + [optionGroup addObject:option]; + hasStyleSection |= [self hasSection:[@"style/" stringByAppendingString:option]]; + } + rime_get_api()->config_end(&optionIter); + if (hasStyleSection) { + for (NSUInteger i = 0; i < optionGroup.count; ++i) { + switcher[optionGroup[i]] = optionGroup[reset]; + optionGroups[optionGroup[i]] = optionGroup; + } + } + } + } + rime_get_api()->config_end(&switchIter); + return [[SquirrelOptionSwitcher alloc] initWithSchemaId:_schemaId + switcher:switcher + optionGroups:optionGroups]; +} + - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { NSString *rootKey = [@"app_options/" stringByAppendingString:appName]; SquirrelMutableAppOptions *appOptions = [[SquirrelMutableAppOptions alloc] init]; diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 424973b44..92d8c0197 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -2,7 +2,9 @@ #import @interface SquirrelInputController : IMKInputController + - (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index; + @end #define kSELECT 0x1 diff --git a/SquirrelInputController.m b/SquirrelInputController.m index e937dd7b6..2d391ff8a 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -11,6 +11,7 @@ @interface SquirrelInputController (Private) - (void)createSession; - (void)destroySession; - (BOOL)rimeConsumeCommittedText; +- (void)updateStyleOptions; - (void)rimeUpdate; - (void)updateAppOptions; @end @@ -162,8 +163,6 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers { - // TODO add special key event preprocessing here - // with linear candidate list, arrow keys may behave differently. Bool is_linear = NSApp.squirrelAppDelegate.panel.linear; if (is_linear != rime_get_api()->get_option(_session, "_linear")) { @@ -482,7 +481,6 @@ - (void)showPanelWithPreedit:(NSString *)preedit caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -510,7 +508,6 @@ - (void)showPanelWithPreedit:(NSString *)preedit caretPos:caretPos candidates:candidates comments:comments - labels:labels highlighted:index pageNum:pageNum lastPage:lastPage @@ -550,8 +547,8 @@ - (void)updateAppOptions NSLog(@"set app option: %@ = %d", key, value); rime_get_api()->set_option(_session, key.UTF8String, value); } - _panellessCommitFix = (appOptions[@"panelless_commit_fix"] ? : @(NO)).boolValue; - _inlinePlaceHolder = (appOptions[@"inline_placeholder"] ? : @(NO)).boolValue; + _panellessCommitFix = [appOptions[@"panelless_commit_fix"] boolValue]; + _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; } } @@ -580,6 +577,40 @@ - (BOOL)rimeConsumeCommittedText return NO; } +- (void)updateStyleOptions +{ + // update the list of switchers that change styles and color-themes + SquirrelOptionSwitcher *optionSwitcher; + SquirrelConfig *schema = [[SquirrelConfig alloc] init]; + if ([schema openWithSchemaId:_schemaId baseConfig:NSApp.squirrelAppDelegate.config] && + [schema hasSection:@"style"]) { + optionSwitcher = [schema getOptionSwitcher]; + } else { + optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:_schemaId switcher:@{} optionGroups:@{}]; + } + [schema close]; + NSMutableDictionary *switcher = [optionSwitcher mutableSwitcher]; + NSSet *prevStates = [NSSet setWithArray:optionSwitcher.optionStates]; + for (NSString *state in prevStates) { + NSString *updatedState; + NSArray *optionGroup = [optionSwitcher.switcher allKeysForObject:state]; + for (NSString *option in optionGroup) { + if (rime_get_api()->get_option(_session, option.UTF8String)) { + updatedState = option; + break; + } + } + updatedState = updatedState ? : [@"!" stringByAppendingString:optionGroup[0]]; + if (![updatedState isEqualToString:state]) { + for (NSString *option in optionGroup) { + switcher[option] = updatedState; + } + } + } + [optionSwitcher updateSwitcher:switcher]; + [NSApp.squirrelAppDelegate.panel setOptionSwitcher:optionSwitcher]; +} + - (void)rimeUpdate { //NSLog(@"rimeUpdate"); @@ -592,17 +623,19 @@ - (void)rimeUpdate RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style - if (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id) != 0) { + if (!switcherMenu && (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { _schemaId = @(status.schema_id); + [self updateStyleOptions]; + [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; // inline preedit _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || - rime_get_api()->get_option(_session, "inline"); + rime_get_api()->get_option(_session, "inline"); _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline")); // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit && !switcherMenu); + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); } rime_get_api()->free_status(&status); } @@ -668,34 +701,13 @@ - (void)rimeUpdate NSMutableArray *comments = [NSMutableArray array]; for (int i = 0; i < ctx.menu.num_candidates; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; - if (ctx.menu.candidates[i].comment) { - [comments addObject:@(ctx.menu.candidates[i].comment)]; - } else { - [comments addObject:@""]; - } - } - NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:ctx.menu.page_size]; - if (ctx.menu.select_keys) { - NSString *selectKeys = [@(ctx.menu.select_keys) stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; - for (int i = 0; i < ctx.menu.page_size; ++i) { - [labels addObject:[selectKeys substringWithRange:NSMakeRange(i, 1)]]; - } - } else if (ctx.select_labels) { - for (int i = 0; i < ctx.menu.page_size; ++i) { - [labels addObject:@(ctx.select_labels[i])]; - } - } else { - NSString *labelString = @"1234567890"; - for (int i = 0; i < ctx.menu.page_size; ++i) { - [labels addObject:[labelString substringWithRange:NSMakeRange(i, 1)]]; - } + [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; } [self showPanelWithPreedit:(_inlinePreedit && !switcherMenu ? nil : preeditText) selRange:NSMakeRange(start, end - start) caretPos:(switcherMenu ? NSNotFound : caretPos) candidates:candidates comments:comments - labels:labels highlighted:ctx.menu.highlighted_candidate_index pageNum:ctx.menu.page_no lastPage:ctx.menu.is_last_page]; diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 6921cb2f1..180b3e7be 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -2,6 +2,7 @@ #import "SquirrelInputController.h" @class SquirrelConfig; +@class SquirrelOptionSwitcher; @interface SquirrelPanel : NSPanel @@ -15,6 +16,8 @@ @property(nonatomic, readonly) BOOL inlinePreedit; // Show first candidate inline @property(nonatomic, readonly) BOOL inlineCandidate; +// Store switch options that change style (color theme) settings +@property(nonatomic, strong) SquirrelOptionSwitcher *optionSwitcher; // position of input caret on screen. @property(nonatomic, assign) NSRect position; @@ -25,7 +28,6 @@ caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -40,4 +42,6 @@ - (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark; +- (void)loadLabelConfig:(SquirrelConfig *)config; + @end diff --git a/SquirrelPanel.m b/SquirrelPanel.m index fbcc9b38b..c29f72326 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -56,17 +56,63 @@ - (CGPathRef)quartzPath { @end +@implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) + +- (void)formatMarkDown { + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; + NSInteger __block offset = 0; + [regex enumerateMatchesInString:self.string options:0 range:NSMakeRange(0, self.length) + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + result = [result resultByAdjustingRangesWithOffset:offset]; + NSString *tag = [self.string substringWithRange:[result rangeAtIndex:1]]; + if ([tag isEqualToString:@"**"] || [tag isEqualToString:@"__"] || + [tag isEqualToString:@""] || [tag isEqualToString:@""]) { + [self applyFontTraits:NSBoldFontMask range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"*"] || [tag isEqualToString:@"_"] || + [tag isEqualToString:@""] || [tag isEqualToString:@""]) { + [self applyFontTraits:NSItalicFontMask range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@""]) { + [self addAttribute:NSUnderlineStyleAttributeName + value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"~~"] || [tag isEqualToString:@""]) { + [self addAttribute:NSStrikethroughStyleAttributeName + value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { + [self superscriptRange:[result rangeAtIndex:5]]; + [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 + usingBlock:^(id value, NSRange range, BOOL *stop) { + NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + [self addAttribute:NSFontAttributeName value:font range:range]; + }]; + } else if ([tag isEqualToString:@"~"] || [tag isEqualToString:@""]) { + [self subscriptRange:[result rangeAtIndex:5]]; + [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 + usingBlock:^(id value, NSRange range, BOOL *stop) { + NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + [self addAttribute:NSFontAttributeName value:font range:range]; + }]; + } + [self deleteCharactersInRange:[result rangeAtIndex:6]]; + [self deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:1].length; + }]; + if (offset != 0) { // no match. text remain unchanged. + [self formatMarkDown]; + } +} + +@end + static const CGFloat kOffsetHeight = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kTipSpecifier = @"%s"; static NSString *const kFullWidthSpace = @" "; @interface SquirrelTheme : NSObject -@property(nonatomic, assign) BOOL native; - @property(nonatomic, strong, readonly) NSColor *backgroundColor; @property(nonatomic, strong, readonly) NSColor *backgroundImage; @property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @@ -75,7 +121,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSColor *borderColor; @property(nonatomic, readonly) CGFloat cornerRadius; -@property(nonatomic, readonly) CGFloat hilitedCornerRadius; +@property(nonatomic, readonly) CGFloat highlightedCornerRadius; @property(nonatomic, readonly) CGFloat separatorWidth; @property(nonatomic, readonly) NSSize edgeInset; @property(nonatomic, readonly) CGFloat linespace; @@ -112,13 +158,11 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; -@property(nonatomic, strong, readonly) NSString *prefixLabelFormat; -@property(nonatomic, strong, readonly) NSString *suffixLabelFormat; +@property(nonatomic, strong, readonly) NSArray *labels; +@property(nonatomic, strong, readonly) NSArray *candidateFormats; +@property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; @property(nonatomic, strong, readonly) NSString *statusMessageType; -- (void)setCandidateFormat:(NSString *)candidateFormat; -- (void)setStatusMessageType:(NSString *)statusMessageType; - - (void)setBackgroundColor:(NSColor *)backgroundColor backgroundImage:(NSColor *)backgroundImage highlightedStripColor:(NSColor *)highlightedStripColor @@ -126,76 +170,133 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor borderColor:(NSColor *)borderColor; -- (void)setCornerRadius:(CGFloat)cornerRadius - hilitedCornerRadius:(CGFloat)hilitedCornerRadius - separatorWidth:(CGFloat)separatorWidth - edgeInset:(NSSize)edgeInset - linespace:(CGFloat)linespace - preeditLinespace:(CGFloat)preeditLinespace - alpha:(CGFloat)alpha - translucency:(CGFloat)translucency - lineLength:(CGFloat)lineLength - showPaging:(BOOL)showPaging - rememberSize:(BOOL)rememberSize - tabled:(BOOL)tabled - linear:(BOOL)linear - vertical:(BOOL)vertical - inlinePreedit:(BOOL)inlinePreedit - inlineCandidate:(BOOL)inlineCandidate; - -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs - statusAttrs:(NSMutableDictionary *)statusAttrs; +- (void) setCornerRadius:(CGFloat)cornerRadius + highlightedCornerRadius:(CGFloat)highlightedCornerRadius + separatorWidth:(CGFloat)separatorWidth + edgeInset:(NSSize)edgeInset + linespace:(CGFloat)linespace + preeditLinespace:(CGFloat)preeditLinespace + alpha:(CGFloat)alpha + translucency:(CGFloat)translucency + lineLength:(CGFloat)lineLength + showPaging:(BOOL)showPaging + rememberSize:(BOOL)rememberSize + tabled:(BOOL)tabled + linear:(BOOL)linear + vertical:(BOOL)vertical + inlinePreedit:(BOOL)inlinePreedit + inlineCandidate:(BOOL)inlineCandidate; + +- (void) setAttrs:(NSDictionary *)attrs + highlightedAttrs:(NSDictionary *)highlightedAttrs + labelAttrs:(NSDictionary *)labelAttrs + labelHighlightedAttrs:(NSDictionary *)labelHighlightedAttrs + commentAttrs:(NSDictionary *)commentAttrs + commentHighlightedAttrs:(NSDictionary *)commentHighlightedAttrs + preeditAttrs:(NSDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSDictionary *)pagingHighlightedAttrs + statusAttrs:(NSDictionary *)statusAttrs; - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle preeditParagraphStyle:(NSParagraphStyle *)preeditParagraphStyle pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; +- (void)setLabels:(NSArray *)labels; + +- (void)setCandidateFormat:(NSString *)candidateFormat; + +- (void)setStatusMessageType:(NSString *)statusMessageType; + @end @implementation SquirrelTheme -- (void)setCandidateFormat:(NSString *)candidateFormat { - // in the candiate format, everything other than '%@' is considered part of the label - NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; - if (candidateRange.location == NSNotFound) { - _prefixLabelFormat = candidateFormat; - _suffixLabelFormat = nil; - return; - } - if (candidateRange.location > 0) { - // everything before '%@' is prefix label - NSRange prefixLabelRange = NSMakeRange(0, candidateRange.location); - _prefixLabelFormat = [candidateFormat substringWithRange:prefixLabelRange]; - } else { - _prefixLabelFormat = nil; - } - if (NSMaxRange(candidateRange) < candidateFormat.length) { - // everything after '%@' is suffix label - NSRange suffixLabelRange = NSMakeRange(NSMaxRange(candidateRange), - candidateFormat.length - NSMaxRange(candidateRange)); - _suffixLabelFormat = [candidateFormat substringWithRange:suffixLabelRange]; - } else { - // '%@' is at the end, so suffix label does not exist - _suffixLabelFormat = nil; +static NSArray * formatLabels(NSAttributedString *format, NSArray *labels) { + NSRange enumRange = NSMakeRange(0, 0); + NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; + NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString:[labels componentsJoinedByString:@""]]; + if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff10, 10)] + isSupersetOfSet:labelCharacters]) { // 01..9 + if ([format.string containsString:@"%c\u20dd"]) { // ①..⑨⓪ + enumRange = [format.string rangeOfString:@"%c\u20dd"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x24ea : [label characterAtIndex:0] - 0xff11 + 0x2460, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } else if ([format.string containsString:@"(%c)"]) { // ⑴..⑼⑽ + enumRange = [format.string rangeOfString:@"(%c)"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x247d : [label characterAtIndex:0] - 0xff11 + 0x2474, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } else if ([format.string containsString:@"%c."]) { // ⒈..⒐🄀 + enumRange = [format.string rangeOfString:@"%c."]; + for (NSString *label in labels) { + if ([label characterAtIndex:0] == 0xff10) { + unichar chars[] = {0xd83c, 0xdd00, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:3]]; + [formatted addObject:[newFormat copy]]; + } else { + unichar chars[] = {[label characterAtIndex:0] - 0xff11 + 0x2488, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } + } else if ([format.string containsString:@"%c,"]) { //🄂..🄊🄁 + enumRange = [format.string rangeOfString:@"%c,"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff10 + 0xdd01, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } + } else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff21, 26)] + isSupersetOfSet:labelCharacters]) { // A..Z + if ([format.string containsString:@"%c\u20dd"]) { // Ⓐ..Ⓩ + enumRange = [format.string rangeOfString:@"%c\u20dd"]; + for (NSString *label in labels) { + unichar chars[] = {[label characterAtIndex:0] - 0xff21 + 0x24b6, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } else if ([format.string containsString:@"(%c)"]) { // 🄐..🄩 + enumRange = [format.string rangeOfString:@"(%c)"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd10, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } else if ([format.string containsString:@"%c\u20de"]) { // 🄰..🅉 + enumRange = [format.string rangeOfString:@"%c\u20de"]; + for (NSString *label in labels) { + unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd30, 0x0}; + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; + [formatted addObject:[newFormat copy]]; + } + } } -} - -- (void)setStatusMessageType:(NSString *)type { - if ([type isEqualToString:@"long"] || [type isEqualToString:@"short"] || [type isEqualToString:@"mix"]) { - _statusMessageType = type; - } else { - _statusMessageType = @"mix"; + if (enumRange.length == 0) { + enumRange = [format.string rangeOfString:@"%c"]; + for (NSString *label in labels) { + NSMutableAttributedString *newFormat = [format mutableCopy]; + [newFormat replaceCharactersInRange:enumRange withString:label]; + [formatted addObject:[newFormat copy]]; + } } + return [formatted copy]; } - (void)setBackgroundColor:(NSColor *)backgroundColor @@ -212,24 +313,24 @@ - (void)setBackgroundColor:(NSColor *)backgroundColor _borderColor = borderColor; } -- (void)setCornerRadius:(CGFloat)cornerRadius - hilitedCornerRadius:(CGFloat)hilitedCornerRadius - separatorWidth:(CGFloat)separatorWidth - edgeInset:(NSSize)edgeInset - linespace:(CGFloat)linespace - preeditLinespace:(CGFloat)preeditLinespace - alpha:(CGFloat)alpha - translucency:(CGFloat)translucency - lineLength:(CGFloat)lineLength - showPaging:(BOOL)showPaging - rememberSize:(BOOL)rememberSize - tabled:(BOOL)tabled - linear:(BOOL)linear - vertical:(BOOL)vertical - inlinePreedit:(BOOL)inlinePreedit - inlineCandidate:(BOOL)inlineCandidate { +- (void) setCornerRadius:(CGFloat)cornerRadius + highlightedCornerRadius:(CGFloat)highlightedCornerRadius + separatorWidth:(CGFloat)separatorWidth + edgeInset:(NSSize)edgeInset + linespace:(CGFloat)linespace + preeditLinespace:(CGFloat)preeditLinespace + alpha:(CGFloat)alpha + translucency:(CGFloat)translucency + lineLength:(CGFloat)lineLength + showPaging:(BOOL)showPaging + rememberSize:(BOOL)rememberSize + tabled:(BOOL)tabled + linear:(BOOL)linear + vertical:(BOOL)vertical + inlinePreedit:(BOOL)inlinePreedit + inlineCandidate:(BOOL)inlineCandidate { _cornerRadius = cornerRadius; - _hilitedCornerRadius = hilitedCornerRadius; + _highlightedCornerRadius = highlightedCornerRadius; _separatorWidth = separatorWidth; _edgeInset = edgeInset; _linespace = linespace; @@ -246,17 +347,17 @@ - (void)setCornerRadius:(CGFloat)cornerRadius _inlineCandidate = inlineCandidate; } -- (void) setAttrs:(NSMutableDictionary *)attrs - highlightedAttrs:(NSMutableDictionary *)highlightedAttrs - labelAttrs:(NSMutableDictionary *)labelAttrs - labelHighlightedAttrs:(NSMutableDictionary *)labelHighlightedAttrs - commentAttrs:(NSMutableDictionary *)commentAttrs - commentHighlightedAttrs:(NSMutableDictionary *)commentHighlightedAttrs - preeditAttrs:(NSMutableDictionary *)preeditAttrs - preeditHighlightedAttrs:(NSMutableDictionary *)preeditHighlightedAttrs - pagingAttrs:(NSMutableDictionary *)pagingAttrs - pagingHighlightedAttrs:(NSMutableDictionary *)pagingHighlightedAttrs - statusAttrs:(NSMutableDictionary *)statusAttrs { +- (void) setAttrs:(NSDictionary *)attrs + highlightedAttrs:(NSDictionary *)highlightedAttrs + labelAttrs:(NSDictionary *)labelAttrs + labelHighlightedAttrs:(NSDictionary *)labelHighlightedAttrs + commentAttrs:(NSDictionary *)commentAttrs + commentHighlightedAttrs:(NSDictionary *)commentHighlightedAttrs + preeditAttrs:(NSDictionary *)preeditAttrs + preeditHighlightedAttrs:(NSDictionary *)preeditHighlightedAttrs + pagingAttrs:(NSDictionary *)pagingAttrs + pagingHighlightedAttrs:(NSDictionary *)pagingHighlightedAttrs + statusAttrs:(NSDictionary *)statusAttrs { _attrs = attrs; _highlightedAttrs = highlightedAttrs; _labelAttrs = labelAttrs; @@ -314,13 +415,13 @@ - (void) setAttrs:(NSMutableDictionary *)attrs NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; symbolAttrsForwardFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶\uFE0E"]; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶\uFE0E" attributes:symbolAttrsForwardFill]; + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶"]; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" attributes:symbolAttrsForwardFill]; NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷\uFE0E"]; - _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷\uFE0E" attributes:symbolAttrsForwardStroke]; + [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷"]; + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" attributes:symbolAttrsForwardStroke]; } } @@ -334,6 +435,61 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle _statusParagraphStyle = statusParagraphStyle; } +- (void)setLabels:(NSArray *)labels { + _labels = labels; +} + +- (void)setCandidateFormat:(NSString *)candidateFormat { + // validate candidate format: must have enumerator '%c' before candidate '%@' + if (![candidateFormat containsString:@"%@"]) { + candidateFormat = [candidateFormat stringByAppendingString:@"%@"]; + } + if (![candidateFormat containsString:@"%c"]) { + candidateFormat = [@"%c" stringByAppendingString:candidateFormat]; + } + NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; + NSRange labelRange = [candidateFormat rangeOfString:@"%c"]; + if (labelRange.location > candidateRange.location) { + candidateFormat = kDefaultCandidateFormat; + candidateRange = [candidateFormat rangeOfString:@"%@"]; + } + labelRange = NSMakeRange(0, candidateRange.location); + NSRange commentRange = NSMakeRange(NSMaxRange(candidateRange), candidateFormat.length - NSMaxRange(candidateRange)); + // parse markdown formats + NSMutableAttributedString *format = [[NSMutableAttributedString alloc] initWithString:candidateFormat]; + NSMutableAttributedString *highlightedFormat = [format mutableCopy]; + [format addAttributes:_labelAttrs range:labelRange]; + [highlightedFormat addAttributes:_labelHighlightedAttrs range:labelRange]; + [format addAttributes:_attrs range:candidateRange]; + [highlightedFormat addAttributes:_highlightedAttrs range:candidateRange]; + if (commentRange.length > 0) { + [format addAttributes:_commentAttrs range:commentRange]; + [highlightedFormat addAttributes:_commentHighlightedAttrs range:commentRange]; + } + [format formatMarkDown]; + [highlightedFormat formatMarkDown]; + // add placeholder for comment '%s' + candidateRange = [format.string rangeOfString:@"%@"]; + commentRange = NSMakeRange(NSMaxRange(candidateRange), format.length - NSMaxRange(candidateRange)); + if (commentRange.length > 0) { + [format replaceCharactersInRange:commentRange withString:[kTipSpecifier stringByAppendingString:[format.string substringWithRange:commentRange]]]; + [highlightedFormat replaceCharactersInRange:commentRange withString:[kTipSpecifier stringByAppendingString:[highlightedFormat.string substringWithRange:commentRange]]]; + } else { + [format appendAttributedString:[[NSAttributedString alloc] initWithString:kTipSpecifier attributes:_commentAttrs]]; + [highlightedFormat appendAttributedString:[[NSAttributedString alloc] initWithString:kTipSpecifier attributes:_commentHighlightedAttrs]]; + } + _candidateFormats = formatLabels(format, _labels); + _candidateHighlightedFormats = formatLabels(highlightedFormat, _labels); +} + +- (void)setStatusMessageType:(NSString *)type { + if ([type isEqualToString:@"long"] || [type isEqualToString:@"short"] || [type isEqualToString:@"mix"]) { + _statusMessageType = type; + } else { + _statusMessageType = @"mix"; + } +} + @end @interface SquirrelView : NSView @@ -350,11 +506,9 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingPaths; @property(nonatomic, readonly) NSUInteger pagingButton; -@property(nonatomic, readonly) BOOL isDark; -@property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, readonly) CAShapeLayer *shape; -@property(nonatomic, getter = isFlipped, readonly) BOOL flipped; -@property(nonatomic, readonly) BOOL wantsUpdateLayer; +@property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; +@property(nonatomic, readonly) BOOL isDark; - (void)drawViewWithInsets:(NSEdgeInsets)insets candidateRanges:(NSArray *)candidateRanges @@ -363,6 +517,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)insets highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange pagingButton:(NSUInteger)pagingButton; + - (NSRect)contentRectForRange:(NSRange)range; @end @@ -442,8 +597,8 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _textView.selectable = NO; _textView.wantsLayer = NO; - _defaultTheme = [[SquirrelTheme alloc] init]; _shape = [[CAShapeLayer alloc] init]; + _defaultTheme = [[SquirrelTheme alloc] init]; if (@available(macOS 10.14, *)) { _darkTheme = [[SquirrelTheme alloc] init]; } @@ -461,7 +616,6 @@ - (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) } } - // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { if (@available(macOS 12.0, *)) { @@ -514,7 +668,7 @@ - (NSRect)contentRectForRange:(NSRange)range { } } -// Will triger - (void)drawRect:(NSRect)dirtyRect +// Will triger - (void)updateLayer - (void)drawViewWithInsets:(NSEdgeInsets)insets candidateRanges:(NSArray *)candidateRanges highlightedIndex:(NSUInteger)highlightedIndex @@ -535,7 +689,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)insets } // Bezier cubic curve, which has continuous roundness -NSBezierPath * drawRoundedPolygon(NSArray *vertex, CGFloat radius) { +static NSBezierPath * drawRoundedPolygon(NSArray *vertex, CGFloat radius) { NSBezierPath *path = [NSBezierPath bezierPath]; if (vertex.count < 1) { return path; @@ -579,14 +733,14 @@ - (void)drawViewWithInsets:(NSEdgeInsets)insets return path; } -NSArray * rectVertex(NSRect rect) { +static NSArray * rectVertex(NSRect rect) { return @[@(rect.origin), @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y))]; } -BOOL nearEmptyRect(NSRect rect) { +static inline BOOL nearEmptyRect(NSRect rect) { return NSHeight(rect) * NSWidth(rect) < 1; } @@ -689,7 +843,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead } // Based on the 3 boxes from multilineRectForRange, calculate the vertex of the polygon containing the text in range -NSArray * multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { +static NSArray * multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && nearEmptyRect(trailingRect)) { return rectVertex(leadingRect); } else if (nearEmptyRect(bodyRect) && nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { @@ -721,7 +875,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead } } -NSColor * hooverColor(NSColor *color, BOOL darkTheme) { +static inline NSColor * hooverColor(NSColor *color, BOOL darkTheme) { if (@available(macOS 10.14, *)) { return [color colorWithSystemEffect:NSColorSystemEffectRollover]; } @@ -732,7 +886,7 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead } } -NSColor * disabledColor(NSColor *color, BOOL darkTheme) { +static inline NSColor * disabledColor(NSColor *color, BOOL darkTheme) { if (@available(macOS 10.14, *)) { return [color colorWithSystemEffect:NSColorSystemEffectDisabled]; } @@ -825,9 +979,9 @@ - (void)updateLayer { } else { highlightedPreeditPoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } - highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); + highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); if (highlightedPreeditPoints2.count > 0) { - [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.hilitedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; + [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; } } } @@ -838,7 +992,7 @@ - (void)updateLayer { candidateBlockRect.origin.x = textContainerRect.origin.x; candidateBlockRect.origin.y += textContainerRect.origin.y; candidateBlockRect = NSIntersectionRect(candidateBlockRect, textContainerRect); - candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.hilitedCornerRadius); + candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.highlightedCornerRadius); // Draw candidate highlight rect if (theme.linear) { @@ -897,16 +1051,16 @@ - (void)updateLayer { } CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar - [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.hilitedCornerRadius)]; - [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.hilitedCornerRadius)]; + [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius)]; + [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.highlightedCornerRadius)]; [candidateVertGridPath closePath]; } CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((tailEdge + theme.separatorWidth / 2 - _insets.left) / tabInterval) * tabInterval + _insets.left; + CGFloat tabPosition = pow(2, ceil(log2((tailEdge - leadOrigin.x) / tabInterval))) * tabInterval + leadOrigin.x; if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + bodyRect.size.width += tabPosition - tailEdge; } else if (NSIsEmptyRect(bodyRect)) { - trailingRect.size.width += tabPosition - theme.separatorWidth / 2 - tailEdge; + trailingRect.size.width += tabPosition - tailEdge; } else { bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); trailingRect = NSZeroRect; @@ -922,9 +1076,9 @@ - (void)updateLayer { } else { candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); if (candidatePoints2.count > 0) { - [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.hilitedCornerRadius)]; + [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.highlightedCornerRadius)]; } _candidatePaths[i] = candidatePath; } @@ -943,7 +1097,7 @@ - (void)updateLayer { } candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); NSArray *candidatePoints = rectVertex(candidateRect); - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.hilitedCornerRadius); + NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); _candidatePaths[i] = candidatePath; } } @@ -969,18 +1123,18 @@ - (void)updateLayer { pageDownRect = NSIntersectionRect(pageDownRect, textContainerRect); pageUpRect = NSIntersectionRect(pageUpRect, textContainerRect); pageDownPath = drawRoundedPolygon(rectVertex(pageDownRect), - MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3)); + MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3)); pageUpPath = drawRoundedPolygon(rectVertex(pageUpRect), - MIN(theme.hilitedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect)) / 3)); + MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect)) / 3)); _pagingPaths[0] = pageUpPath; _pagingPaths[1] = pageDownPath; } // Draw borders backgroundPath = drawRoundedPolygon(rectVertex(backgroundRect), - MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3)); + MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3)); textContainerPath = drawRoundedPolygon(rectVertex(textContainerRect), - MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect) / 3)); + MIN(theme.highlightedCornerRadius, NSHeight(textContainerRect) / 3)); if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { borderPath = [backgroundPath copy]; [borderPath appendBezierPath:textContainerPath]; @@ -994,7 +1148,7 @@ - (void)updateLayer { CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; textContainerLayer.path = [textContainerPath quartzPath]; textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; - textContainerLayer.cornerRadius = MIN(theme.hilitedCornerRadius, NSHeight(textContainerRect) / 3); + textContainerLayer.cornerRadius = MIN(theme.highlightedCornerRadius, NSHeight(textContainerRect) / 3); [self.layer setSublayers:nil]; self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); CALayer *panelLayer = [[CALayer alloc] init]; @@ -1085,13 +1239,13 @@ - (void)updateLayer { CAShapeLayer *horzGridLayer = [[CAShapeLayer alloc] init]; horzGridLayer.path = [candidateHorzGridPath quartzPath]; horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; - horzGridLayer.lineWidth = 0.5; + horzGridLayer.lineWidth = theme.edgeInset.height / 2; horzGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:horzGridLayer]; CAShapeLayer *vertGridLayer = [[CAShapeLayer alloc] init]; vertGridLayer.path = [candidateVertGridPath quartzPath]; vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; - vertGridLayer.lineWidth = 1; + vertGridLayer.lineWidth = theme.edgeInset.width / 2; vertGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:vertGridLayer]; } @@ -1142,13 +1296,14 @@ @implementation SquirrelPanel { NSUInteger _caretPos; NSArray *_candidates; NSArray *_comments; - NSArray *_labels; NSUInteger _index; NSUInteger _pageNum; NSUInteger _turnPage; BOOL _lastPage; + BOOL _mouseDown; NSPoint _scrollLocus; + BOOL _initPosition; NSString *_statusMessage; NSTimer *_statusTimer; @@ -1190,23 +1345,22 @@ + (NSColor *)accentColor { if (@available(macOS 10.14, *)) { return [NSColor controlAccentColor]; } else { - return [NSColor colorForControlTint:[[self class] currentControlTint]]; + return [NSColor colorForControlTint:[NSColor currentControlTint]]; } } - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; - theme.native = YES; - theme.candidateFormat = kDefaultCandidateFormat; - NSColor *secondaryTextColor = [[self class] secondaryTextColor]; - NSColor *accentColor = [[self class] accentColor]; + NSColor *secondaryTextColor = [SquirrelPanel secondaryTextColor]; + NSColor *accentColor = [SquirrelPanel accentColor]; NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; - defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); // solve terminal hijack when non-inline + // prevent mac terminal from hijacking non-alphabetic keys on non-inline mode + defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; @@ -1215,6 +1369,9 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *highlightedAttrs = [defaultAttrs mutableCopy]; highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedMenuItemTextColor]; highlightedAttrs[NSFontAttributeName] = userFont; + // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. + attrs[NSWritingDirectionAttributeName] = @[@(0)]; + highlightedAttrs[NSWritingDirectionAttributeName] = @[@(0)]; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; labelAttrs[NSForegroundColorAttributeName] = accentColor; @@ -1273,21 +1430,25 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs - labelAttrs:labelAttrs - labelHighlightedAttrs:labelHighlightedAttrs - commentAttrs:commentAttrs - commentHighlightedAttrs:commentHighlightedAttrs - preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs - statusAttrs:statusAttrs]; + [theme setAttrs:attrs + highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs + labelHighlightedAttrs:labelHighlightedAttrs + commentAttrs:commentAttrs + commentHighlightedAttrs:commentHighlightedAttrs + preeditAttrs:preeditAttrs + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs + statusAttrs:statusAttrs]; + [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle pagingParagraphStyle:pagingParagraphStyle statusParagraphStyle:statusParagraphStyle]; + + [theme setLabels:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"0"]]; + [theme setCandidateFormat:kDefaultCandidateFormat]; } - (instancetype)init { @@ -1298,8 +1459,6 @@ - (instancetype)init { if (self) { self.alphaValue = 1.0; - // _window.level = NSScreenSaverWindowLevel + 1; - // ^ May fix visibility issue in fullscreen games. self.level = kCGCursorWindowLevel - 10; self.hasShadow = NO; self.opaque = NO; @@ -1325,6 +1484,7 @@ - (instancetype)init { [self initializeUIStyleForDarkMode:YES]; } _maxSize = NSZeroSize; + _initPosition = YES; } return self; } @@ -1380,19 +1540,20 @@ - (void)sendEvent:(NSEvent *)event { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { [_inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments labels:_labels highlighted:cursorIndex - pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments highlighted:cursorIndex + pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments labels:_labels highlighted:_index - pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments highlighted:_index + pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; } } } break; case NSEventTypeLeftMouseDragged: { _mouseDown = NO; + _maxSize = NSZeroSize; // reset the remember_size references after moving the panel [self performWindowDragWithEvent:event]; } break; case NSEventTypeScrollWheel: { @@ -1460,7 +1621,8 @@ - (void)getTextWidthLimit { // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { if (@available(macOS 10.14, *)) { - NSAppearance *requestedAppearance = [NSAppearance appearanceNamed:(_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; + NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: + (_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; } @@ -1478,8 +1640,8 @@ - (void)show { BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; NSRect maxContentRect = contentRect; - if (theme.lineLength > 0) { // fixed line length / text width - if (_maxSize.width > 0) { // not applicable to status message where maxSize is set to 0 + if (theme.lineLength > 0) { // fixed line length (text width) + if (_statusMessage == nil) { // not applicable to status message maxContentRect.size.width = _textWidthLimit; } } @@ -1505,58 +1667,68 @@ - (void)show { } } + _initPosition |= NSIntersectsRect(self.frame, _position); NSRect windowRect; if (theme.vertical) { windowRect.size = NSMakeSize(NSHeight(maxContentRect) + insets.top + insets.bottom, NSWidth(maxContentRect) + insets.left + insets.right); - // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -kOffsetHeight) - NSHeight(windowRect); + if (_initPosition ) { + // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa + if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -kOffsetHeight) - NSHeight(windowRect); + } else { + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); + } + // Make the right edge of candidate block fixed at the left of cursor + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); + if (!sweepVertical && _view.preeditRange.length > 0) { + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); + } } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); - } - // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); - if (!sweepVertical && _view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += NSHeight(preeditRect) + insets.top; + windowRect.origin.x = NSMaxX(self.frame) - NSWidth(windowRect); + windowRect.origin.y = NSMaxY(self.frame) - NSHeight(windowRect); } } else { windowRect.size = NSMakeSize(NSWidth(maxContentRect) + insets.left + insets.right, NSHeight(maxContentRect) + insets.top + insets.bottom); - if (sweepVertical) { - // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + if (_initPosition) { + if (sweepVertical) { + // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa + if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + } else { + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + } + windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, + NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); } - windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, - NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + windowRect.origin = NSMakePoint(NSMinX(self.frame), NSMaxY(self.frame) - NSHeight(windowRect)); } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = sweepVertical ? NSMinY(screenRect) : NSMaxY(_position) + kOffsetHeight; + windowRect.origin.y = _initPosition && !sweepVertical ? NSMaxY(_position) + kOffsetHeight : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (sweepVertical ? NSMaxY(screenRect) : NSMinY(_position) - kOffsetHeight) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? NSMinY(_position) - kOffsetHeight : NSMaxY(screenRect)) - NSHeight(windowRect); } if (theme.vertical) { - windowRect.origin.x += NSHeight(maxContentRect) - NSHeight(contentRect); - windowRect.size.width -= NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.origin.x += round(NSHeight(maxContentRect) - NSHeight(contentRect)); + windowRect.size.width -= round(NSHeight(maxContentRect) - NSHeight(contentRect)); } else { - windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); - windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.origin.y += round(NSHeight(maxContentRect) - NSHeight(contentRect)); + windowRect.size.height -= round(NSHeight(maxContentRect) - NSHeight(contentRect)); } // rotate the view, the core in vertical mode! @@ -1590,6 +1762,7 @@ - (void)show { } [self setAlphaValue:theme.alpha]; [self orderFront:nil]; + _initPosition = NO; // voila ! } @@ -1600,6 +1773,7 @@ - (void)hide { } [self orderOut:nil]; _maxSize = NSZeroSize; + _initPosition = YES; } - (void)setLayoutForRange:(NSRange)charRange @@ -1735,125 +1909,12 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng } } -static NSArray * formatLabels(NSString *format, NSArray *labels) { - NSString *newFormat; - NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; - NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString:[labels componentsJoinedByString:@""]]; - if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff10, 10)] - isSupersetOfSet:labelCharacters]) { // 01..9 - if ([format containsString:@"%c\u20dd"]) { // ①..⑨⓪ - newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20dd" withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x24ea : [label characterAtIndex:0] - 0xff11 + 0x2460, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } else if ([format containsString:@"(%c)"]) { // ⑴..⑼⑽ - newFormat = [format stringByReplacingOccurrencesOfString:@"(%c)" withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x247d : [label characterAtIndex:0] - 0xff11 + 0x2474, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } else if ([format containsString:@"%c."]) { // ⒈..⒐🄀 - newFormat = [format stringByReplacingOccurrencesOfString:@"%c." withString:@"%S"]; - for (NSString *label in labels) { - if ([label characterAtIndex:0] == 0xff10) { - unichar chars[] = {0xd83c, 0xdd00, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } else { - unichar chars[] = {[label characterAtIndex:0] - 0xff11 + 0x2488, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } - } else if ([format containsString:@"%c,"]) { //🄂..🄊🄁 - newFormat = [format stringByReplacingOccurrencesOfString:@"%c," withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff10 + 0xdd01, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } - } else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff21, 26)] - isSupersetOfSet:labelCharacters]) { // A..Z - if ([format containsString:@"%c\u20dd"]) { // Ⓐ..Ⓩ - newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20dd" withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] - 0xff21 + 0x24b6, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } else if ([format containsString:@"(%c)"]) { // 🄐..🄩 - newFormat = [format stringByReplacingOccurrencesOfString:@"(%c)" withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd10, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } else if ([format containsString:@"%c\u20de"]) { // 🄰..🅉 - newFormat = [format stringByReplacingOccurrencesOfString:@"%c\u20de" withString:@"%S"]; - for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd30, 0x0}; - [formatted addObject:[NSString stringWithFormat:newFormat, chars]]; - } - } - } - if (!newFormat) { - newFormat = [format stringByReplacingOccurrencesOfString:@"%c" withString:@"%@"]; - for (NSString *label in labels) { - [formatted addObject:[NSString stringWithFormat:newFormat, label]]; - } - } - return formatted; -} - -NSMutableAttributedString * markDownText(NSMutableAttributedString* text) { - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; - NSUInteger __block offset = 0; - [regex enumerateMatchesInString:text.mutableString options:0 range:NSMakeRange(0, text.length) - usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { - result = [result resultByAdjustingRangesWithOffset:offset]; - NSString *tag = [text.mutableString substringWithRange:[result rangeAtIndex:1]]; - if ([tag isEqualToString:@"**"] || [tag isEqualToString:@"__"] || - [tag isEqualToString:@""] || [tag isEqualToString:@""]) { - [text applyFontTraits:NSBoldFontMask range:[result rangeAtIndex:5]]; - } else if ([tag isEqualToString:@"*"] || [tag isEqualToString:@"_"] || - [tag isEqualToString:@""] || [tag isEqualToString:@""]) { - [text applyFontTraits:NSItalicFontMask range:[result rangeAtIndex:5]]; - } else if ([tag isEqualToString:@""]) { - [text addAttribute:NSUnderlineStyleAttributeName - value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; - } else if ([tag isEqualToString:@"~~"] || [tag isEqualToString:@""]) { - [text addAttribute:NSStrikethroughStyleAttributeName - value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; - } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { - [text superscriptRange:[result rangeAtIndex:5]]; - [text enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(id value, NSRange range, BOOL *stop) { - NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; - [text addAttribute:NSFontAttributeName value:font range:range]; - }]; - } else if ([tag isEqualToString:@"~"] || [tag isEqualToString:@""]) { - [text subscriptRange:[result rangeAtIndex:5]]; - [text enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(id value, NSRange range, BOOL *stop) { - NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; - [text addAttribute:NSFontAttributeName value:font range:range]; - }]; - } - [text deleteCharactersInRange:[result rangeAtIndex:6]]; - [text deleteCharactersInRange:[result rangeAtIndex:1]]; - offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:1].length; - }]; - if (offset == 0) { // no match. text remain unchanged. - return text; - } else { - return markDownText(text); - } -} - // Main function to add attributes to text output from librime - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - labels:(NSArray *)labels highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage @@ -1865,23 +1926,13 @@ - (void)showPreedit:(NSString *)preedit _caretPos = caretPos; _candidates = candidates; _comments = comments; - _labels = labels; _index = index; _pageNum = pageNum; _lastPage = lastPage; } - NSUInteger numCandidates = candidates.count; - if (numCandidates == 0) { - _index = index = NSNotFound; - } - _turnPage = turnPage; - if (_turnPage == NSPageUpFunctionKey) { - turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } else if (_turnPage == NSPageDownFunctionKey) { - turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; - } - [self getTextWidthLimit]; + [self getTextWidthLimit]; + NSUInteger numCandidates = candidates.count; if (numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { @@ -1898,6 +1949,16 @@ - (void)showPreedit:(NSString *)preedit return; } + if (numCandidates == 0) { + _index = index = NSNotFound; + } + _turnPage = turnPage; + if (_turnPage == NSPageUpFunctionKey) { + turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; + } else if (_turnPage == NSPageDownFunctionKey) { + turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + } + SquirrelTheme *theme = _view.currentTheme; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -1916,7 +1977,6 @@ - (void)showPreedit:(NSString *)preedit NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); NSMutableParagraphStyle *paragraphStyleCandidate; - NSArray *prefixLabels, *suffixLabels; // preedit if (preedit) { @@ -1962,69 +2022,66 @@ - (void)showPreedit:(NSString *)preedit if (theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle copy]; } - if (theme.prefixLabelFormat != nil) { - prefixLabels = formatLabels(theme.prefixLabelFormat, labels); - } - if (theme.suffixLabelFormat != nil) { - suffixLabels = formatLabels(theme.suffixLabelFormat, labels); - } - for (NSUInteger i = 0; i < candidates.count; ++i) { - NSMutableAttributedString *item = [[NSMutableAttributedString alloc] init]; - NSDictionary *attrs = (i == index) ? theme.highlightedAttrs : theme.attrs; - NSDictionary *labelAttrs = (i == index) ? theme.labelHighlightedAttrs : theme.labelAttrs; - NSDictionary *commentAttrs = (i == index) ? theme.commentHighlightedAttrs : theme.commentAttrs; - CGFloat labelWidth = 0.0; - - if (theme.prefixLabelFormat != nil) { - [item appendAttributedString:[[NSAttributedString alloc] - initWithString:prefixLabels[i] attributes:labelAttrs]]; - if (!theme.linear) { // get the label size for indent - labelWidth = markDownText(item).size.width; - } - } + CGFloat tabInterval = theme.separatorWidth * 2; + for (NSUInteger idx = 0; idx < candidates.count; ++idx) { + // attributed labels are already included in candidateFormats + NSMutableAttributedString *item = (idx == index) ? [theme.candidateHighlightedFormats[idx] mutableCopy] : [theme.candidateFormats[idx] mutableCopy]; + NSRange candidateRange = [item.string rangeOfString:@"%@"]; + // get the label size for indent + CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange:NSMakeRange(0, candidateRange.location)].size.width); - NSUInteger candidateStart = item.length; - [item appendAttributedString:[[NSAttributedString alloc] - initWithString:candidates[i] attributes:attrs]]; - // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - [item addAttribute:NSWritingDirectionAttributeName value:@[@0] - range:NSMakeRange(candidateStart, item.length - candidateStart)]; - - if (i < comments.count && [comments[i] length] != 0) { - NSString *comment = [@" " stringByAppendingString:comments[i]]; - [item appendAttributedString:[[NSAttributedString alloc] - initWithString:comment attributes:commentAttrs]]; - } + [item replaceCharactersInRange:candidateRange withString:candidates[idx]]; - if (theme.suffixLabelFormat != nil) { - [item appendAttributedString:[[NSAttributedString alloc] - initWithString:suffixLabels[i] attributes:labelAttrs]]; + NSRange commentRange = [item.string rangeOfString:kTipSpecifier]; + if (idx < comments.count && [comments[idx] length] != 0) { + [item replaceCharactersInRange:commentRange withString:[@" " stringByAppendingString:comments[idx]]]; + } else { + [item deleteCharactersInRange:commentRange]; } + [item formatMarkDown]; if (!theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.headIndent = labelWidth; } - [markDownText(item) addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, item.length)]; + [item addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleCandidate + range:NSMakeRange(0, item.length)]; + // determine if the line is too wide and line break is needed, based on screen size. - if (i > 0) { + if (lineStart != text.length) { NSUInteger separatorStart = text.length; - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.attrs]; - [separator addAttribute:NSVerticalGlyphFormAttributeName value:@NO + NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.commentAttrs]; + if (theme.tabled) { + CGFloat widthInTabs = (ceil([text attributedSubstringFromRange:candidateRanges.lastObject.rangeValue].size.width) + theme.separatorWidth) / tabInterval; + NSUInteger numPaddingTabs = pow(2, ceil(log2(widthInTabs))) - ceil(widthInTabs); + [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; + } + [separator addAttribute:NSVerticalGlyphFormAttributeName value:@(NO) range:NSMakeRange(0, separator.length)]; NSRange separatorRange = NSMakeRange(separatorStart, separator.length); [text appendAttributedString:separator]; [text appendAttributedString:item]; - if (theme.linear && [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { + if (theme.linear && (ceil(item.size.width) > _textWidthLimit || [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)])) { [text replaceCharactersInRange:separatorRange withString:theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\n"] : @"\n"]; lineStart = separatorStart + (theme.tabled ? 2 : 1); } - } else { + } else { // at the start of a new line, no need to determine line break [text appendAttributedString:item]; } - [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length - item.length, item.length)]]; + // for linear layout, middle-truncate candidates that are longer than one line + if (theme.linear && ceil(item.size.width) > _textWidthLimit) { + if (idx < numCandidates - 1 || theme.showPaging) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; + } + NSMutableParagraphStyle *paragraphStyleTruncating = [paragraphStyleCandidate mutableCopy]; + paragraphStyleTruncating.lineBreakMode = NSLineBreakByTruncatingMiddle; + [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleTruncating range:NSMakeRange(lineStart, item.length)]; + [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(lineStart, text.length - lineStart - 1)]]; + lineStart = text.length; + } else { + [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length - item.length, item.length)]]; + } } // paging indication @@ -2041,7 +2098,7 @@ - (void)showPreedit:(NSString *)preedit [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 1, 1)]; } - [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.attrs]]; + [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.commentAttrs]]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; [text appendAttributedString:paging]; @@ -2053,13 +2110,12 @@ - (void)showPreedit:(NSString *)preedit } if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - CGFloat tabInterval = theme.separatorWidth * 2; if (theme.tabled) { - maxLineLength = ceil((maxLineLength) / tabInterval) * tabInterval - theme.separatorWidth; + maxLineLength = ceil(maxLineLength / tabInterval) * tabInterval - theme.separatorWidth; } else { [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; } - CGFloat candidateEndPosition = [text attributedSubstringFromRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)].size.width; + CGFloat candidateEndPosition = ceil([text attributedSubstringFromRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)].size.width); NSMutableArray *tabStops = [[NSMutableArray alloc] init]; for (NSUInteger i = 1; tabInterval * i < candidateEndPosition; ++i) { [tabStops addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:tabInterval * i]]; @@ -2087,7 +2143,6 @@ - (void)showPreedit:(NSString *)preedit } typesetter: - [text addAttribute:NSStrokeWidthAttributeName value:@(1 - self.backingScaleFactor) range:NSMakeRange(0, text.length)]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; if (preedit) { [self setLayoutForRange:preeditRange @@ -2140,22 +2195,22 @@ - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)message - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - _maxSize = NSZeroSize; // disable remember_size and fixed line_length for status messages - CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:kFullWidthSpace attributes:theme.statusAttrs].size.width; - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2, - theme.edgeInset.height, theme.edgeInset.width + separatorWidth / 2); + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width + theme.separatorWidth / 2, + theme.edgeInset.height, theme.edgeInset.width + theme.separatorWidth / 2); _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; - [text addAttribute:NSStrokeWidthAttributeName value:@(1 - self.backingScaleFactor) range:NSMakeRange(0, text.length)]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; [self setLayoutForRange:NSMakeRange(0, text.length) withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) paragraphStyle:theme.statusParagraphStyle]; + // disable remember_size and fixed line_length for status messages + _initPosition = YES; + _maxSize = NSZeroSize; if (_statusTimer) { [_statusTimer invalidate]; } @@ -2282,41 +2337,78 @@ static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, } } +- (void)loadLabelConfig:(SquirrelConfig *)config { + SquirrelTheme *theme = [_view selectTheme:NO]; + [SquirrelPanel updateTheme:theme withLabelConfig:config]; + if (@available(macOS 10.14, *)) { + SquirrelTheme *darkTheme = [_view selectTheme:YES]; + [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; + } +} + ++ (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config { + int menuSize = [config getInt:@"menu/page_size"] ? : 5; + NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; + NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; + if (selectKeys) { + NSString *keyCaps = [[selectKeys uppercaseString] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + for (int i = 0; i < menuSize; ++i) { + labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; + } + } else { + NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; + if (selectLabels) { + for (int i = 0; i < menuSize; ++i) { + labels[i] = selectLabels[i]; + } + } else { + NSString *numerals = @"1234567890"; + for (int i = 0; i < menuSize; ++i) { + labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; + } + } + } + [theme setLabels:labels]; +} + - (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; - [[self class] updateTheme:theme withConfig:config forDarkMode:isDark]; + NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; + [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forDarkMode:isDark]; } -+ (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { ++ (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config styleOptions:(NSSet *)styleOptions forDarkMode:(BOOL)isDark { + // INTERFACE BOOL linear = NO; BOOL tabled = NO; BOOL vertical = NO; updateCandidateListLayout(&linear, &tabled, config, @"style"); updateTextOrientation(&vertical, config, @"style"); - BOOL inlinePreedit = [config getBool:@"style/inline_preedit"]; - BOOL inlineCandidate = [config getBool:@"style/inline_candidate"]; - BOOL showPaging = [config getBool:@"style/show_paging"]; - BOOL rememberSize = [config getBool:@"style/remember_size"]; + NSNumber *inlinePreedit = [config getOptionalBool:@"style/inline_preedit"]; + NSNumber *inlineCandidate = [config getOptionalBool:@"style/inline_candidate"]; + NSNumber *showPaging = [config getOptionalBool:@"style/show_paging"]; + NSNumber *rememberSize = [config getOptionalBool:@"style/remember_size"]; NSString *statusMessageType = [config getString:@"style/status_message_type"]; NSString *candidateFormat = [config getString:@"style/candidate_format"]; - + // TYPOGRAPHY NSString *fontName = [config getString:@"style/font_face"]; - CGFloat fontSize = MAX([config getDouble:@"style/font_point"], 0.0); + NSNumber *fontSize = [config getOptionalDouble:@"style/font_point"]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - CGFloat labelFontSize = MAX([config getDouble:@"style/label_font_point"], 0.0); + NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point"]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - CGFloat commentFontSize = MAX([config getDouble:@"style/comment_font_point"], 0.0); - CGFloat alpha = MIN(MAX([config getDouble:@"style/alpha"], 0.0), 1.0); - CGFloat translucency = MIN(MAX([config getDouble:@"style/translucency"], 0.0), 1.0); - CGFloat cornerRadius = MAX([config getDouble:@"style/corner_radius"], 0.0); - CGFloat hilitedCornerRadius = MAX([config getDouble:@"style/hilited_corner_radius"], 0.0); - CGFloat borderHeight = MAX([config getDouble:@"style/border_height"], 0.0); - CGFloat borderWidth = MAX([config getDouble:@"style/border_width"], 0.0); - CGFloat lineSpacing = MAX([config getDouble:@"style/line_spacing"], 0.0); - CGFloat spacing = MAX([config getDouble:@"style/spacing"], 0.0); - CGFloat baseOffset = [config getDouble:@"style/base_offset"]; - CGFloat lineLength = MAX([config getDouble:@"style/line_length"], 0.0); - + NSNumber *commentFontSize = [config getOptionalDouble:@"style/comment_font_point"]; + NSNumber *alpha = [config getOptionalDouble:@"style/alpha"]; + NSNumber *translucency = [config getOptionalDouble:@"style/translucency"]; + NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius"]; + NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius"]; + NSNumber *borderHeight = [config getOptionalDouble:@"style/border_height"]; + NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width"]; + NSNumber *lineSpacing = [config getOptionalDouble:@"style/line_spacing"]; + NSNumber *spacing = [config getOptionalDouble:@"style/spacing"]; + NSNumber *baseOffset = [config getOptionalDouble:@"style/base_offset"]; + NSNumber *lineLength = [config getOptionalDouble:@"style/line_length"]; + // CHROMATICS NSColor *backgroundColor; NSColor *backgroundImage; NSColor *borderColor; @@ -2334,193 +2426,124 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f NSString *colorScheme; if (isDark) { - colorScheme = [config getString:@"style/color_scheme_dark"]; + for (NSString *option in styleOptions) { + if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; + } + colorScheme = colorScheme ? : [config getString:@"style/color_scheme_dark"]; } if (!colorScheme) { - colorScheme = [config getString:@"style/color_scheme"]; + for (NSString *option in styleOptions) { + if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme", option]])) break; + } + colorScheme = colorScheme ? : [config getString:@"style/color_scheme"]; } BOOL isNative = !colorScheme || [colorScheme isEqualToString:@"native"]; - if (!isNative) { - NSString *prefix = [@"preset_color_schemes/" stringByAppendingString:colorScheme]; + NSArray *configPrefixes = isNative ? [@"style/" stringsByAppendingPaths:styleOptions.allObjects] : + [[NSArray arrayWithObject:[@"preset_color_schemes/" stringByAppendingString:colorScheme]] + arrayByAddingObjectsFromArray:[@"style/" stringsByAppendingPaths:styleOptions.allObjects]]; + + // get color scheme and then check possible overrides from styleSwitcher + for (NSString *prefix in configPrefixes) { + // CHROMATICS override if (@available(macOS 10.12, *)) { - config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]]; - } - backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]]; - backgroundImage = [config getPattern:[prefix stringByAppendingString:@"/back_image"]]; - borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]]; - preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]]; - textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]]; - highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]]; - if (highlightedTextColor == nil) { - highlightedTextColor = textColor; - } - highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]]; - candidateTextColor = [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]]; - if (candidateTextColor == nil) { - // in non-inline mode, 'text_color' is for rendering preedit text. - // if not otherwise specified, candidate text is also rendered in this color. - candidateTextColor = textColor; - } - highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]]; - if (highlightedCandidateTextColor == nil) { - highlightedCandidateTextColor = highlightedTextColor; - } - highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_back_color"]]; - if (highlightedCandidateBackColor == nil) { - highlightedCandidateBackColor = highlightedBackColor; - } - commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]]; - highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_comment_text_color"]]; + config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; + } + backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backgroundColor; + backgroundImage = [config getPattern:[prefix stringByAppendingString:@"/back_image"]] ? : backgroundImage; + borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; + preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackgroundColor; + textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]] ? : textColor; + highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]] ? : highlightedTextColor; + highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]] ? : highlightedBackColor; + candidateTextColor = [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]] ? : candidateTextColor; + highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]] ? : highlightedCandidateTextColor; + highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_back_color"]] ? : highlightedCandidateBackColor; + commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]] ? : commentTextColor; + highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_comment_text_color"]] ? : highlightedCommentTextColor; + candidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_color"]] ? : candidateLabelColor; + // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid + highlightedCandidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]] ? : + [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]] ? : highlightedCandidateLabelColor; // the following per-color-scheme configurations, if exist, will // override configurations with the same name under the global 'style' section - + // INTERFACE override updateCandidateListLayout(&linear, &tabled, config, prefix); updateTextOrientation(&vertical, config, prefix); - - NSNumber *inlinePreeditOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]]; - if (inlinePreeditOverridden) { - inlinePreedit = inlinePreeditOverridden.boolValue; - } - NSNumber *inlineCandidateOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]]; - if (inlineCandidateOverridden) { - inlineCandidate = inlineCandidateOverridden.boolValue; - } - NSNumber *showPagingOverridden = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]]; - if (showPagingOverridden) { - showPaging = showPagingOverridden.boolValue; - } - NSString *candidateFormatOverridden = [config getString:[prefix stringByAppendingString:@"/candidate_format"]]; - if (candidateFormatOverridden) { - candidateFormat = candidateFormatOverridden; - } - NSString *fontNameOverridden = [config getString:[prefix stringByAppendingString:@"/font_face"]]; - if (fontNameOverridden) { - fontName = fontNameOverridden; - } - NSNumber *fontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]]; - if (fontSizeOverridden) { - fontSize = MAX(fontSizeOverridden.doubleValue, 0.0); - } - NSString *labelFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/label_font_face"]]; - if (labelFontNameOverridden) { - labelFontName = labelFontNameOverridden; - } - NSNumber *labelFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]]; - if (labelFontSizeOverridden) { - labelFontSize = MAX(labelFontSizeOverridden.doubleValue, 0.0); - } - NSString *commentFontNameOverridden = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]]; - if (commentFontNameOverridden) { - commentFontName = commentFontNameOverridden; - } - NSNumber *commentFontSizeOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]]; - if (commentFontSizeOverridden) { - commentFontSize = MAX(commentFontSizeOverridden.doubleValue, 0.0); - } - NSColor *candidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_color"]]; - if (candidateLabelColorOverridden) { - candidateLabelColor = candidateLabelColorOverridden; - } - NSColor *highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]]; - if (highlightedCandidateLabelColorOverridden == nil) { - // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid - highlightedCandidateLabelColorOverridden = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]]; - } - if (highlightedCandidateLabelColorOverridden) { - highlightedCandidateLabelColor = highlightedCandidateLabelColorOverridden; - } - NSNumber *alphaOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]]; - if (alphaOverridden) { - alpha = MIN(MAX(alphaOverridden.doubleValue, 0.0), 1.0); - } - NSNumber *translucencyOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"]]; - if (translucencyOverridden) { - translucency = MIN(MAX(translucencyOverridden.doubleValue, 0.0), 1.0); - } - NSNumber *cornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]]; - if (cornerRadiusOverridden) { - cornerRadius = MAX(cornerRadiusOverridden.doubleValue, 0.0); - } - NSNumber *hilitedCornerRadiusOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]]; - if (hilitedCornerRadiusOverridden) { - hilitedCornerRadius = MAX(hilitedCornerRadiusOverridden.doubleValue, 0.0); - } - NSNumber *borderHeightOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]]; - if (borderHeightOverridden) { - borderHeight = MAX(borderHeightOverridden.doubleValue, 0.0); - } - NSNumber *borderWidthOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]]; - if (borderWidthOverridden) { - borderWidth = MAX(borderWidthOverridden.doubleValue, 0.0); - } - NSNumber *lineSpacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]]; - if (lineSpacingOverridden) { - lineSpacing = MAX(lineSpacingOverridden.doubleValue, 0.0); - } - NSNumber *spacingOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]]; - if (spacingOverridden) { - spacing = MAX(spacingOverridden.doubleValue, 0.0); - } - NSNumber *baseOffsetOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]]; - if (baseOffsetOverridden) { - baseOffset = baseOffsetOverridden.doubleValue; - } - NSNumber *lineLengthOverridden = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_length"]]; - if (lineLengthOverridden) { - lineLength = MAX(lineLengthOverridden.doubleValue, 0.0); - } - } - - fontSize = fontSize ? : kDefaultFontSize; + inlinePreedit = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]] ? : inlinePreedit; + inlineCandidate = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]] ? : inlineCandidate; + showPaging = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]] ? : showPaging; + rememberSize = [config getOptionalBool:[prefix stringByAppendingString:@"/remember_size"]] ? : rememberSize; + statusMessageType = [config getString:[prefix stringByAppendingString:@"style/status_message_type"]] ? : statusMessageType; + candidateFormat = [config getString:[prefix stringByAppendingString:@"/candidate_format"]] ? : candidateFormat; + // TYPOGRAPHY override + fontName = [config getString:[prefix stringByAppendingString:@"/font_face"]] ? : fontName; + fontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]] ? : fontSize; + labelFontName = [config getString:[prefix stringByAppendingString:@"/label_font_face"]] ? : labelFontName; + labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]] ? : labelFontSize; + commentFontName = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]] ? : commentFontName; + commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]] ? : commentFontSize; + alpha = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]] ? : alpha; + translucency = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"]] ? : translucency; + cornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]] ? : cornerRadius; + highlightedCornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]] ? : highlightedCornerRadius; + borderHeight = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]] ? : borderHeight; + borderWidth = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]] ? : borderWidth; + lineSpacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]] ? : lineSpacing; + spacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]] ? : spacing; + baseOffset = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]] ? : baseOffset; + lineLength = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_length"]] ? : lineLength; + } + + // TYPOGRAPHY refinement + fontSize = fontSize ? : @(kDefaultFontSize); labelFontSize = labelFontSize ? : fontSize; commentFontSize = commentFontSize ? : fontSize; + NSDictionary *monoDigitAttrs = @{NSFontFeatureSettingsAttribute: + @[@{NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector)}, + @{NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), + NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector)}] }; NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); - NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0.0].fontName)) - size:fontSize]; - - NSDictionary *monoDigitAttrs = @{ - NSFontFeatureSettingsAttribute: - @[@{ NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector) }, - @{ NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector) }] - }; + NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0].fontName)) + size:MAX(fontSize.doubleValue, 0)]; + NSFontDescriptor *labelFontDescriptor = [(getFontDescriptor(labelFontName) ? : fontDescriptor) fontDescriptorByAddingAttributes:monoDigitAttrs]; - NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize] - : [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:MAX(labelFontSize.doubleValue, 0)] + : [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) - size:commentFontSize]; + size:MAX(commentFontSize.doubleValue, 0)]; NSFont *pagingFont; if (@available(macOS 12.0, *)) { - pagingFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize weight:NSFontWeightRegular]; + pagingFont = [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; } else { - pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] + pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:labelFontSize]; + size:MAX(labelFontSize.doubleValue, 0)]; } CGFloat fontHeight = getLineHeight(font, vertical); CGFloat labelFontHeight = getLineHeight(labelFont, vertical); CGFloat commentFontHeight = getLineHeight(commentFont, vertical); CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); - CGFloat separatorWidth = [[NSAttributedString alloc] initWithString:kFullWidthSpace attributes:@{NSFontAttributeName: font}].size.width; + CGFloat separatorWidth = ceil([[NSAttributedString alloc] initWithString:kFullWidthSpace + attributes:@{NSFontAttributeName: commentFont}].size.width); NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; preeditParagraphStyle.minimumLineHeight = fontHeight; preeditParagraphStyle.maximumLineHeight = fontHeight; - preeditParagraphStyle.paragraphSpacing = spacing; + preeditParagraphStyle.paragraphSpacing = MAX(spacing.doubleValue, 0); NSMutableParagraphStyle *paragraphStyle = [theme.paragraphStyle mutableCopy]; paragraphStyle.minimumLineHeight = lineHeight; paragraphStyle.maximumLineHeight = lineHeight; - paragraphStyle.paragraphSpacing = lineSpacing / 2; - paragraphStyle.paragraphSpacingBefore = lineSpacing / 2; + paragraphStyle.paragraphSpacing = MAX(lineSpacing.doubleValue / 2, 0); + paragraphStyle.paragraphSpacingBefore = MAX(lineSpacing.doubleValue / 2, 0); paragraphStyle.tabStops = @[]; paragraphStyle.defaultTabInterval = separatorWidth * 2; @@ -2561,19 +2584,28 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; labelHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; - attrs[NSBaselineOffsetAttributeName] = @(baseOffset); - highlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - labelHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - commentHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - preeditAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - pagingAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); - statusAttrs[NSBaselineOffsetAttributeName] = @(baseOffset); + attrs[NSBaselineOffsetAttributeName] = baseOffset; + highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; + labelAttrs[NSBaselineOffsetAttributeName] = baseOffset; + labelHighlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; + commentAttrs[NSBaselineOffsetAttributeName] = baseOffset; + commentHighlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; + preeditAttrs[NSBaselineOffsetAttributeName] = baseOffset; + preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; + pagingAttrs[NSBaselineOffsetAttributeName] = baseOffset; + statusAttrs[NSBaselineOffsetAttributeName] = baseOffset; + + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; + + labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); + pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); - NSColor *secondaryTextColor = [[self class] secondaryTextColor]; - NSColor *accentColor = [[self class] accentColor]; + // CHROMATICS refinement + NSColor *secondaryTextColor = [SquirrelPanel secondaryTextColor]; + NSColor *accentColor = [SquirrelPanel accentColor]; if (@available(macOS 10.14, *)) { if (theme.translucency > 0 && @@ -2621,48 +2653,37 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedTextColor; statusAttrs[NSForegroundColorAttributeName] = commentTextColor; - preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; - - labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); - pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); - - NSSize edgeInset = vertical ? NSMakeSize(borderHeight, borderWidth) : NSMakeSize(borderWidth, borderHeight); - - [theme setCornerRadius:MIN(cornerRadius, lineHeight / 2) - hilitedCornerRadius:MIN(hilitedCornerRadius, lineHeight / 3) - separatorWidth:separatorWidth - edgeInset:edgeInset - linespace:lineSpacing - preeditLinespace:spacing - alpha:(alpha == 0 ? 1.0 : alpha) - translucency:translucency - lineLength:lineLength - showPaging:showPaging - rememberSize:rememberSize - tabled:tabled - linear:linear - vertical:vertical - inlinePreedit:inlinePreedit - inlineCandidate:inlineCandidate]; - - theme.native = isNative; - [theme setCandidateFormat:candidateFormat ? candidateFormat : kDefaultCandidateFormat]; - [theme setStatusMessageType:statusMessageType]; - - [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs - labelAttrs:labelAttrs - labelHighlightedAttrs:labelHighlightedAttrs - commentAttrs:commentAttrs - commentHighlightedAttrs:commentHighlightedAttrs - preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs - statusAttrs:statusAttrs]; + NSSize edgeInset = vertical ? NSMakeSize(MAX(borderHeight.doubleValue, 0), MAX(borderWidth.doubleValue, 0)) : + NSMakeSize(MAX(borderWidth.doubleValue, 0), MAX(borderHeight.doubleValue, 0)); + + [theme setCornerRadius:MIN(cornerRadius.doubleValue, lineHeight / 2) + highlightedCornerRadius:MIN(highlightedCornerRadius.doubleValue, lineHeight / 3) + separatorWidth:separatorWidth + edgeInset:edgeInset + linespace:MAX(lineSpacing.doubleValue, 0) + preeditLinespace:MAX(spacing.doubleValue, 0) + alpha:(alpha ? MIN(MAX(alpha.doubleValue, 0.0), 1.0) : 1.0) + translucency:(translucency ? MIN(MAX(translucency.doubleValue, 0.0), 1.0) : 0.0) + lineLength:lineLength.doubleValue ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 + showPaging:showPaging.boolValue + rememberSize:rememberSize.boolValue + tabled:tabled + linear:linear + vertical:vertical + inlinePreedit:inlinePreedit.boolValue + inlineCandidate:inlineCandidate.boolValue]; + + [theme setAttrs:attrs + highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs + labelHighlightedAttrs:labelHighlightedAttrs + commentAttrs:commentAttrs + commentHighlightedAttrs:commentHighlightedAttrs + preeditAttrs:preeditAttrs + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs + statusAttrs:statusAttrs]; [theme setParagraphStyle:paragraphStyle preeditParagraphStyle:preeditParagraphStyle @@ -2675,6 +2696,9 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config f highlightedPreeditColor:highlightedBackColor preeditBackgroundColor:preeditBackgroundColor borderColor:borderColor]; + + [theme setCandidateFormat:candidateFormat ? : kDefaultCandidateFormat]; + [theme setStatusMessageType:statusMessageType]; } @end diff --git a/main.m b/main.m index bc6887748..fba429e0b 100644 --- a/main.m +++ b/main.m @@ -80,8 +80,12 @@ int main(int argc, char *argv[]) { NSLog(@"Problematic launch detected!"); NSArray *args = @[@"-v", NSLocalizedString(@"say_voice", nil), NSLocalizedString(@"problematic_launch", nil)]; - [NSTask launchedTaskWithExecutableURL:[[NSURL alloc] initFileURLWithPath:@"/usr/bin/say"] - arguments:args error:nil terminationHandler:nil]; + if (@available(macOS 10.13, *)) { + [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:@"/usr/bin/say"] + arguments:args error:nil terminationHandler:nil]; + } else { + [NSTask launchedTaskWithLaunchPath:@"/usr/bin/say" arguments:args]; + } } else { [NSApp.squirrelAppDelegate setupRime]; [NSApp.squirrelAppDelegate startRimeWithFullCheck:NO]; From c051850fca02cb8fa94e028622168ae72bb5c382 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 14 Oct 2023 13:34:49 +0200 Subject: [PATCH 144/164] chore --- .gitignore | 1 - .gitmodules | 1 + Assets.xcassets/rime.imageset/Contents.json | 3 + Sparkle | 2 +- Squirrel.xcodeproj/project.pbxproj | 8 - SquirrelApplicationDelegate.m | 4 +- SquirrelConfig.m | 16 +- SquirrelInputController.m | 160 ++++++++++++-------- SquirrelPanel.m | 17 ++- action-install.sh | 4 +- autobuild.sh | 9 +- data/squirrel.yaml | 2 + input_source.m | 51 ++++--- librime | 2 +- 14 files changed, 167 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index 7e82d7229..001d6bbaa 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,3 @@ package/test-* *~ .*.swp .DS_Store -rime-with-plugins-1.7.3-osx.zip diff --git a/.gitmodules b/.gitmodules index 1ebd84b73..ec8d933eb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,7 @@ path = librime url = https://github.com/groverlynn/librime.git branch = test + ignore = dirty [submodule "plum"] path = plum url = https://github.com/groverlynn/plum.git diff --git a/Assets.xcassets/rime.imageset/Contents.json b/Assets.xcassets/rime.imageset/Contents.json index 7bb660934..f980ff3d6 100644 --- a/Assets.xcassets/rime.imageset/Contents.json +++ b/Assets.xcassets/rime.imageset/Contents.json @@ -8,5 +8,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" } } diff --git a/Sparkle b/Sparkle index 9684a433e..8fb9c83ad 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 9684a433ee0b55da607791c56f501975f5e11ae5 +Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index d2c7d93e9..04b36e8e0 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -96,8 +96,6 @@ F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */; }; F4E457C82AD97412003F6D79 /* hk2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B72AD97412003F6D79 /* hk2emoji.json */; }; F4E457C92AD97412003F6D79 /* tw2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B82AD97412003F6D79 /* tw2emoji.json */; }; - F4E457CC2AD97499003F6D79 /* TWPhrases.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */; }; - F4E457CD2AD97499003F6D79 /* TWPhrasesRev.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -303,8 +301,6 @@ F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryT.ocd2; sourceTree = ""; }; F4E457B72AD97412003F6D79 /* hk2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hk2emoji.json; sourceTree = ""; }; F4E457B82AD97412003F6D79 /* tw2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tw2emoji.json; sourceTree = ""; }; - F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrases.ocd2; sourceTree = ""; }; - F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = TWPhrasesRev.ocd2; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -452,8 +448,6 @@ 77AA67DE2588916300A592E2 /* t2tw.json */, 77AA67E32588916300A592E2 /* TSCharacters.ocd2 */, 77AA67F62588916400A592E2 /* TSPhrases.ocd2 */, - F4E457CA2AD97498003F6D79 /* TWPhrases.ocd2 */, - F4E457CB2AD97498003F6D79 /* TWPhrasesRev.ocd2 */, 77AA67DF2588916300A592E2 /* tw2s.json */, 77AA67F12588916300A592E2 /* TWVariants.ocd2 */, 77AA67F42588916400A592E2 /* TWVariantsRev.ocd2 */, @@ -578,7 +572,6 @@ files = ( F4E457BB2AD97412003F6D79 /* hk2t.json in Resources */, F4E457C42AD97412003F6D79 /* en2emoji.json in Resources */, - F4E457CC2AD97499003F6D79 /* TWPhrases.ocd2 in Resources */, F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Resources */, 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, F4E457BA2AD97412003F6D79 /* t2emoji.json in Resources */, @@ -599,7 +592,6 @@ F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Resources */, F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Resources */, F4E457BD2AD97412003F6D79 /* s2emoji.json in Resources */, - F4E457CD2AD97499003F6D79 /* TWPhrasesRev.ocd2 in Resources */, 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Resources */, ); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 59f5dee6e..ddca55cbf 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -90,8 +90,8 @@ void notification_handler(void *context_object, RimeSessionId session_id, } } if ([app_delegate enableNotifications]) { - RimeStringSlice state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, NO); - RimeStringSlice state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, YES); + RimeStringSlice state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, False); + RimeStringSlice state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, True); if (state_label_long.str || state_label_short.str) { const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; show_status_message(state_label_long.str, short_message, message_type); diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 3c40058e6..b69c0915a 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -238,9 +238,11 @@ - (NSColor *)getPattern:(NSString *)option { } - (NSArray *)getList:(NSString *)option { - NSMutableArray *strList = [[NSMutableArray alloc] init]; RimeConfigIterator iterator; - rime_get_api()->config_begin_list(&iterator, &_config, option.UTF8String); + if (!rime_get_api()->config_begin_list(&iterator, &_config, option.UTF8String)) { + return nil; + } + NSMutableArray *strList = [[NSMutableArray alloc] init]; while (rime_get_api()->config_next(&iterator)) { [strList addObject:[self getString:@(iterator.path)]]; } @@ -249,10 +251,12 @@ - (NSColor *)getPattern:(NSString *)option { } - (SquirrelOptionSwitcher *)getOptionSwitcher { + RimeConfigIterator switchIter; + if (!rime_get_api()->config_begin_list(&switchIter, &_config, "switches")) { + return nil; + } NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; NSMutableDictionary *> *optionGroups = [[NSMutableDictionary alloc] init]; - RimeConfigIterator switchIter; - rime_get_api()->config_begin_list(&switchIter, &_config, "switches"); while (rime_get_api()->config_next(&switchIter)) { int reset = [self getInt:[@(switchIter.path) stringByAppendingString:@"/reset"]]; NSString *name = [self getString:[@(switchIter.path) stringByAppendingString:@"/name"]]; @@ -291,7 +295,9 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { NSString *rootKey = [@"app_options/" stringByAppendingString:appName]; SquirrelMutableAppOptions *appOptions = [[SquirrelMutableAppOptions alloc] init]; RimeConfigIterator iterator; - rime_get_api()->config_begin_map(&iterator, &_config, rootKey.UTF8String); + if (!rime_get_api()->config_begin_map(&iterator, &_config, rootKey.UTF8String)) { + return nil; + } while (rime_get_api()->config_next(&iterator)) { //NSLog(@"DEBUG option[%d]: %s (%s)", iterator.index, iterator.key, iterator.path); BOOL value = [self getBool:@(iterator.path)]; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 2d391ff8a..ad84b1ed1 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -92,8 +92,15 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); _lastModifier = modifiers; switch (keyCode) { - case kVK_CapsLock: { - rime_modifiers ^= kLockMask; + case kVK_CapsLock: + if (!_goodOldCapsLock) { + set_caps_lock_led(false); + Bool caps_lock_on = rime_get_api()->get_option(_session, "caps_lock_on"); + rime_modifiers = caps_lock_on ? rime_modifiers | kLockMask : rime_modifiers & ~kLockMask; + rime_get_api()->set_option(_session, "caps_lock_on", !caps_lock_on); + } else { + rime_modifiers ^= kLockMask; + } [self processKey:rime_keycode modifiers:rime_modifiers]; } break; case kVK_Shift: @@ -140,7 +147,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents - int rime_keycode = osx_keycode_to_rime_keycode(keyCode, keyChars.UTF8String[0], + int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], modifiers & OSX_SHIFT_MASK, modifiers & OSX_CAPITAL_MASK); if (rime_keycode) { @@ -183,8 +190,7 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers BOOL isVimBackInCommandMode = rime_keycode == XK_Escape || ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || rime_keycode == XK_C || rime_keycode == XK_bracketleft)); - if (isVimBackInCommandMode && - rime_get_api()->get_option(_session, "vim_mode") && + if (isVimBackInCommandMode && rime_get_api()->get_option(_session, "vim_mode") && !rime_get_api()->get_option(_session, "ascii_mode")) { rime_get_api()->set_option(_session, "ascii_mode", True); // NSLog(@"turned Chinese mode off in vim-like editor's command mode"); @@ -195,11 +201,10 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (handled) { BOOL is_chording_key = (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || - rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; - if (is_chording_key && - rime_get_api()->get_option(_session, "_chord_typing")) { + rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || + rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; } else if ((rime_modifiers & kReleaseMask) == 0) { // non-chording key pressed @@ -210,20 +215,17 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers return handled; } -- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index { - BOOL handled = NO; - if (index == NSPageUpFunctionKey && action == kSELECT) { - handled = rime_get_api()->process_key(_session, XK_Page_Up, 0); - } else if (index == NSPageDownFunctionKey && action == kSELECT) { - handled = rime_get_api()->process_key(_session, XK_Page_Down, 0); - } else if (index >= 0 && index < 10) { - if (action == kSELECT) { - handled = rime_get_api()->select_candidate_on_current_page(_session, (int)index); - } else if (action == kCHOOSE) { - handled = rime_get_api()->choose_candidate_on_current_page(_session, (int)index); - } else if (action == kDELETE) { - handled = rime_get_api()->delete_candidate_on_current_page(_session, (int)index); - } +- (void)perform:(rimeAction)action + onIndex:(rimeIndex)index { + bool handled = false; + if (action == kSELECT && ((index >= '!' && index <= '~') || + index == kPageUp || index == kPageDown || index == kEscape)) { + handled = rime_get_api()->process_key(_session, (int)index, 0); + } else if (action == kCHOOSE && index >= '!' && index <= '~') { + handled = rime_get_api()->process_key(_session, (int)index, kAltMask); + } else if (action == kDELETE && index >= 0 && index < 10) { + // kDELETE takes ordinal digits (instead of characters) as indexes + handled= rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index); } if (handled && action != kCHOOSE) { [self rimeUpdate]; @@ -348,6 +350,7 @@ - (void)commitComposition:(id)sender //NSLog(@"commitComposition:"); if (_session) { [self commitString:[self composedString:sender]]; + [self hidePalettes]; rime_get_api()->clear_composition(_session); } } @@ -524,28 +527,25 @@ @implementation SquirrelInputController (Private) - (void)createSession { NSString *app = [self.client bundleIdentifier]; - NSLog(@"createSession: %@", app); - _currentApp = [app copy]; + //NSLog(@"createSession: %@", app); _session = rime_get_api()->create_session(); - _schemaId = nil; if (_session) { - [self updateAppOptions]; - } -} - -- (void)updateAppOptions -{ - if (!_currentApp) { - return; - } - SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:_currentApp]; - if (appOptions) { - for (NSString *key in appOptions) { - BOOL value = appOptions[key].boolValue; - NSLog(@"set app option: %@ = %d", key, value); - rime_get_api()->set_option(_session, key.UTF8String, value); + char *rime_client = NULL; + if (!rime_get_api()->get_property(_session, "client", rime_client, 100) || + ![app isEqualToString:@(rime_client)]) { + rime_get_api()->set_property(_session, "client", app.UTF8String); + SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:app]; + if (appOptions) { + for (NSString *key in appOptions) { + Bool value = [appOptions[key] intValue]; + //NSLog(@"set app option: %@ = %d", key, value); + rime_get_api()->set_option(_session, key.UTF8String, value); + } + _panellessCommitFix = [appOptions[@"panelless_commit_fix"] boolValue]; + _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; + } } _panellessCommitFix = [appOptions[@"panelless_commit_fix"] boolValue]; _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; @@ -618,30 +618,39 @@ - (void)rimeUpdate return; } - BOOL switcherMenu = rime_get_api()->get_option(_session, "dumb"); + BOOL switcherMenu = RimeGetOption(_session, "dumb"); RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style if (!switcherMenu && (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { _schemaId = @(status.schema_id); - [self updateStyleOptions]; - [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; - [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; - // inline preedit - _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && - !rime_get_api()->get_option(_session, "no_inline")) || - rime_get_api()->get_option(_session, "inline"); - _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && - !rime_get_api()->get_option(_session, "no_inline")); - // if not inline, embed soft cursor in preedit string - rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); + _showingSwitcherMenu = rime_get_api()->get_option(_session, "dumb"); + if (!_showingSwitcherMenu) { + [self updateStyleOptions]; + [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; + [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; + // inline preedit + _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && + !rime_get_api()->get_option(_session, "no_inline")) || + rime_get_api()->get_option(_session, "inline"); + _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && + !rime_get_api()->get_option(_session, "no_inline")); + // if not inline, embed soft cursor in preedit string + rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); + } else { + [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:@""]; + } } rime_get_api()->free_status(&status); } RIME_STRUCT(RimeContext, ctx); if (rime_get_api()->get_context(_session, &ctx)) { + // update raw input + const char *raw_input = rime_get_api()->get_input(_session); + [_originalString setString:raw_input ? @(raw_input) : @""]; + // update preedit text const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; @@ -649,7 +658,7 @@ - (void)rimeUpdate // update composed string if (!preedit || switcherMenu) { _composedString = @""; - } else if (rime_get_api()->get_option(_session, "soft_cursor")) { + } else if (RimeGetOption(_session, "soft_cursor")) { int cursorPos = ctx.composition.cursor_pos; char composed[strlen(preedit) - 3]; for (size_t i = 0; i < strlen(preedit) - 3; ++i) { @@ -661,7 +670,7 @@ - (void)rimeUpdate } // update raw input - const char *raw_input = rime_get_api()->get_input(_session); + const char *raw_input = RimeGetInput(_session); _originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; NSUInteger start = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; @@ -708,9 +717,42 @@ - (void)rimeUpdate caretPos:(switcherMenu ? NSNotFound : caretPos) candidates:candidates comments:comments - highlighted:ctx.menu.highlighted_candidate_index - pageNum:ctx.menu.page_no - lastPage:ctx.menu.is_last_page]; + highlighted:(NSUInteger)ctx.menu.highlighted_candidate_index + pageNum:(NSUInteger)ctx.menu.page_no + lastPage:(BOOL)ctx.menu.is_last_page]; + + if (!_showingSwitcherMenu) { + if (_inlineCandidate) { + const char *candidatePreview = ctx.commit_text_preview; + NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; + if (_inlinePreedit) { + if ((caretPos >= end) && (caretPos < length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString: + [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; + } + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) + caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (length - caretPos)]; + } else { + if ((end < caretPos) && (caretPos > start)) { + candidatePreviewText = [candidatePreviewText substringWithRange: + NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; + } else if ((end < length) && (caretPos < end)) { + candidatePreviewText = [candidatePreviewText substringWithRange: + NSMakeRange(0, candidatePreviewText.length - (length - end))]; + } + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) + caretPos:(caretPos < end ? caretPos : candidatePreviewText.length)]; + } + } else { + if (_inlinePreedit && !_showingSwitcherMenu) { + [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; + } else { + [self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0]; + } + } + } rime_get_api()->free_context(&ctx); } else { [NSApp.squirrelAppDelegate.panel hide]; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index c29f72326..4fc684d7a 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1540,14 +1540,14 @@ - (void)sendEvent:(NSEvent *)event { if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { [_inputController perform:kCHOOSE onIndex:cursorIndex]; _index = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:cursorIndex - pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments highlighted:cursorIndex + pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:_index - pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos + candidates:_candidates comments:_comments highlighted:_index + pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; } } } break; @@ -1558,7 +1558,8 @@ - (void)sendEvent:(NSEvent *)event { } break; case NSEventTypeScrollWheel: { SquirrelTheme *theme = _view.currentTheme; - CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; + CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + + [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; if (event.phase == NSEventPhaseBegan) { _scrollLocus = NSZeroPoint; } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && @@ -2023,7 +2024,7 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate = [theme.paragraphStyle copy]; } CGFloat tabInterval = theme.separatorWidth * 2; - for (NSUInteger idx = 0; idx < candidates.count; ++idx) { + for (NSUInteger idx = 0; idx < numCandidates; ++idx) { // attributed labels are already included in candidateFormats NSMutableAttributedString *item = (idx == index) ? [theme.candidateHighlightedFormats[idx] mutableCopy] : [theme.candidateFormats[idx] mutableCopy]; NSRange candidateRange = [item.string rangeOfString:@"%@"]; diff --git a/action-install.sh b/action-install.sh index 8f17d2e4e..68bdc5126 100755 --- a/action-install.sh +++ b/action-install.sh @@ -2,8 +2,8 @@ set -e -rime_version=1.8.5 -rime_git_hash=08dd95f +rime_version=1.9.0-m +rime_git_hash=275b399 rime_archive="rime-${rime_git_hash}-macOS.tar.bz2" rime_download_url="https://github.com/rime/librime/releases/download/${rime_version}/${rime_archive}" diff --git a/autobuild.sh b/autobuild.sh index 9691c4f9e..a971e9d06 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -4,14 +4,13 @@ make clean clean-deps bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua +export CMAKE_GENERATOR=Ninja export BUILD_UNIVERSAL=1 bash librime/install-boost.sh export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" -export CMAKE_GENERATOR=Ninja -export PATH="/opt/homebrew/opt/llvm/bin:/usr/local/opt/llvm/bin:$PATH" -make -C librime ARCHS="arm64;x86_64" test -make -C librime ARCHS="arm64;x86_64" install +# export PATH="/opt/homebrew/opt/llvm/bin:/usr/local/opt/llvm/bin:$PATH" +make -C librime make deps -make ARCHS="arm64;x86_64" install +make install diff --git a/data/squirrel.yaml b/data/squirrel.yaml index a18ef0abc..7b179b940 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -28,7 +28,9 @@ style: #candidate_list_layout: stacked # stacked | linear | tabled text_orientation: horizontal # horizontal | vertical inline_preedit: true + inline_candidate: false show_paging : true + # remember the panel size after it expands towards, rather than away from, the screen center remember_size: true corner_radius: 10 diff --git a/input_source.m b/input_source.m index 0acd1db33..d13664fc2 100644 --- a/input_source.m +++ b/input_source.m @@ -27,10 +27,10 @@ void RegisterInputSource(void) { void ActivateInputSource(int enabled_modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { - TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( - sourceList, i)); - NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceID)); + TISInputSourceRef inputSource = + (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); + NSString *sourceID = (__bridge NSString *) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if (([sourceID isEqualToString:kHansInputModeID] && ((enabled_modes & HANS_INPUT_MODE) != 0)) || @@ -43,8 +43,8 @@ void ActivateInputSource(int enabled_modes) { NSLog(@"Error %d. Failed to enable input mode: %@", enableError, sourceID); } else { NSLog(@"Enabled input mode: %@", sourceID); - CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceIsSelectCapable); + CFBooleanRef isSelectable = (CFBooleanRef) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsSelectCapable); if (CFBooleanGetValue(isSelectable)) { OSStatus selectError = TISSelectInputSource(inputSource); if (selectError) { @@ -62,16 +62,16 @@ void ActivateInputSource(int enabled_modes) { void DeactivateInputSource(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { - TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( - sourceList, i - 1)); - NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceID)); + TISInputSourceRef inputSource = + (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); + NSString *sourceID = (__bridge NSString *) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if ([sourceID isEqualToString:kHansInputModeID] || [sourceID isEqualToString:kHantInputModeID] || [sourceID isEqualToString:kCantInputModeID]) { - CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceIsEnabled)); + CFBooleanRef isEnabled = (CFBooleanRef) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { OSStatus disableError = TISDisableInputSource(inputSource); if (disableError) { @@ -89,16 +89,16 @@ int GetEnabledInputModes(void) { int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { - TISInputSourceRef inputSource = (TISInputSourceRef)(CFArrayGetValueAtIndex( - sourceList, i)); - NSString *sourceID = (__bridge NSString *)(TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceID)); + TISInputSourceRef inputSource = + (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); + NSString *sourceID = (__bridge NSString *) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if ([sourceID isEqualToString:kHansInputModeID] || [sourceID isEqualToString:kHantInputModeID] || [sourceID isEqualToString:kCantInputModeID]) { - CFBooleanRef isEnabled = (CFBooleanRef)(TISGetInputSourceProperty( - inputSource, kTISPropertyInputSourceIsEnabled)); + CFBooleanRef isEnabled = (CFBooleanRef) + TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { if ([sourceID isEqualToString:kHansInputModeID]) { input_modes |= HANS_INPUT_MODE; @@ -112,7 +112,10 @@ int GetEnabledInputModes(void) { } CFRelease(sourceList); if (input_modes != 0) { - NSLog(@"Enabled Input Modes: %d", input_modes); + NSLog(@"Enabled Input Modes:%s%s%s", + input_modes & HANS_INPUT_MODE ? " Hans" : "", + input_modes & HANT_INPUT_MODE ? " Hant" : "", + input_modes & CANT_INPUT_MODE ? " Cant" : ""); } else { NSArray *languages = [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK"]]; @@ -127,10 +130,16 @@ int GetEnabledInputModes(void) { } } if (input_modes != 0) { - NSLog(@"Preferred Input Mode: %d", input_modes); + NSLog(@"Preferred Input Mode:%s%s%s", + input_modes & HANS_INPUT_MODE ? " Hans" : "", + input_modes & HANT_INPUT_MODE ? " Hant" : "", + input_modes & CANT_INPUT_MODE ? " Cant" : ""); } else { input_modes = HANS_INPUT_MODE; - NSLog(@"Default Input Mode: %d", input_modes); + NSLog(@"Default Input Mode:%s%s%s", + input_modes & HANS_INPUT_MODE ? " Hans" : "", + input_modes & HANT_INPUT_MODE ? " Hant" : "", + input_modes & CANT_INPUT_MODE ? " Cant" : ""); } } return input_modes; diff --git a/librime b/librime index 8d842922e..3062c63db 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 8d842922e5dcc0c4dcfd492696ec6ab1c4c5e2c1 +Subproject commit 3062c63db487c1b83083f46a93d588db4b3c3ceb From 59d670f93b2b4c9ee8eaa79795e9becc31c3c2c0 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 21 Oct 2023 03:19:42 +0200 Subject: [PATCH 145/164] 10.15 --- SquirrelApplicationDelegate.m | 14 +-- SquirrelPanel.m | 218 ++++++++++++---------------------- librime | 2 +- main.m | 8 +- 4 files changed, 85 insertions(+), 157 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index ddca55cbf..89548cef8 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -149,9 +149,7 @@ - (void)loadSettings { _enableNotifications = ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; [self.panel loadConfig:_config forDarkMode:NO]; - if (@available(macOS 10.14, *)) { - [self.panel loadConfig:_config forDarkMode:YES]; - } + [self.panel loadConfig:_config forDarkMode:YES]; } - (void)loadSchemaSpecificSettings:(NSString *)schemaId { @@ -162,16 +160,10 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { if ([schema openWithSchemaId:schemaId baseConfig:self.config] && [schema hasSection:@"style"]) { [self.panel loadConfig:schema forDarkMode:NO]; + [self.panel loadConfig:schema forDarkMode:YES]; } else { [self.panel loadConfig:self.config forDarkMode:NO]; - } - if (@available(macOS 10.14, *)) { - if ([schema openWithSchemaId:schemaId baseConfig:self.config] && - [schema hasSection:@"style"]) { - [self.panel loadConfig:schema forDarkMode:YES]; - } else { - [self.panel loadConfig:self.config forDarkMode:YES]; - } + [self.panel loadConfig:self.config forDarkMode:YES]; } [schema close]; } diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 4fc684d7a..1e8ed997f 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -19,14 +19,14 @@ - (CGPathRef)quartzPath { BOOL didClosePath = YES; for (NSUInteger i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { - case NSMoveToBezierPathElement: + case NSBezierPathElementMoveTo: CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); break; - case NSLineToBezierPathElement: + case NSBezierPathElementLineTo: CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); didClosePath = NO; break; - case NSCurveToBezierPathElement: + case NSBezierPathElementCurveTo: CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); @@ -37,7 +37,7 @@ - (CGPathRef)quartzPath { points[1].x, points[1].y); didClosePath = NO; break; - case NSClosePathBezierPathElement: + case NSBezierPathElementClosePath: CGPathCloseSubpath(path); didClosePath = YES; break; @@ -402,10 +402,14 @@ - (void) setAttrs:(NSDictionary *)attrs if (_vertical || !_linear) { symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] - symbolFont.leading); } - NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; - symbolAttrsBackFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4966" forFont:symbolFont baseString:@"◀"]; + NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; + NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; + NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; + symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE92 forFont:symbolFont baseString:@"◀"]; //gid4966 + symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE95 forFont:symbolFont baseString:@"◁"]; //gid4969 + symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE93 forFont:symbolFont baseString:@"▶"]; //gid4967 + symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE94 forFont:symbolFont baseString:@"▷"]; //gid4968 _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; @@ -537,10 +541,8 @@ - (BOOL)wantsUpdateLayer { } - (BOOL)isDark { - if (@available(macOS 10.14, *)) { - if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { - return YES; - } + if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { + return YES; } return NO; } @@ -599,9 +601,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { _shape = [[CAShapeLayer alloc] init]; _defaultTheme = [[SquirrelTheme alloc] init]; - if (@available(macOS 10.14, *)) { - _darkTheme = [[SquirrelTheme alloc] init]; - } + _darkTheme = [[SquirrelTheme alloc] init]; return self; } @@ -875,28 +875,6 @@ - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)lead } } -static inline NSColor * hooverColor(NSColor *color, BOOL darkTheme) { - if (@available(macOS 10.14, *)) { - return [color colorWithSystemEffect:NSColorSystemEffectRollover]; - } - if (darkTheme) { - return [color highlightWithLevel:0.3]; - } else { - return [color shadowWithLevel:0.3]; - } -} - -static inline NSColor * disabledColor(NSColor *color, BOOL darkTheme) { - if (@available(macOS 10.14, *)) { - return [color colorWithSystemEffect:NSColorSystemEffectDisabled]; - } - if (darkTheme) { - return [color shadowWithLevel:0.3]; - } else { - return [color highlightWithLevel:0.3]; - } -} - // All draws happen here - (void)updateLayer { NSBezierPath *backgroundPath; @@ -1057,6 +1035,9 @@ - (void)updateLayer { } CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); CGFloat tabPosition = pow(2, ceil(log2((tailEdge - leadOrigin.x) / tabInterval))) * tabInterval + leadOrigin.x; + if (i == _candidateRanges.count - 1 && pagingRange.length > 0 && tailEdge < pagingLineRect.origin.x) { + tabPosition = MIN(tabPosition, floor((pagingLineRect.origin.x - leadOrigin.x) / tabInterval) * tabInterval + leadOrigin.x); + } if (NSIsEmptyRect(trailingRect)) { bodyRect.size.width += tabPosition - tailEdge; } else if (NSIsEmptyRect(bodyRect)) { @@ -1186,10 +1167,8 @@ - (void)updateLayer { [panelLayer addSublayer:candidateLayer]; } } - if (@available(macOS 10.14, *)) { - if (theme.translucency > 0) { - panelLayer.opacity = 1.0 - theme.translucency; - } + if (theme.translucency > 0) { + panelLayer.opacity = 1.0 - theme.translucency; } if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; @@ -1206,22 +1185,22 @@ - (void)updateLayer { if (pagingRange.length > 0 && buttonColor) { CAShapeLayer *pagingLayer = [[CAShapeLayer alloc] init]; switch (_pagingButton) { - case NSPageUpFunctionKey: { + case NSPageUpFunctionKey: pagingLayer.path = [pageUpPath quartzPath]; - pagingLayer.fillColor = [hooverColor(buttonColor, self.isDark) CGColor]; - } break; - case NSBeginFunctionKey: { + pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; + break; + case NSBeginFunctionKey: pagingLayer.path = [pageUpPath quartzPath]; - pagingLayer.fillColor = [disabledColor(buttonColor, self.isDark) CGColor]; - } break; - case NSPageDownFunctionKey: { + pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; + break; + case NSPageDownFunctionKey: pagingLayer.path = [pageDownPath quartzPath]; - pagingLayer.fillColor = [hooverColor(buttonColor, self.isDark) CGColor]; - } break; - case NSEndFunctionKey: { + pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; + break; + case NSEndFunctionKey: pagingLayer.path = [pageDownPath quartzPath]; - pagingLayer.fillColor = [disabledColor(buttonColor, self.isDark) CGColor]; - } break; + pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; + break; } pagingLayer.mask = textContainerLayer; [self.layer addSublayer:pagingLayer]; @@ -1333,27 +1312,9 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -+ (NSColor *)secondaryTextColor { - if (@available(macOS 10.10, *)) { - return [NSColor secondaryLabelColor]; - } else { - return [NSColor disabledControlTextColor]; - } -} - -+ (NSColor *)accentColor { - if (@available(macOS 10.14, *)) { - return [NSColor controlAccentColor]; - } else { - return [NSColor colorForControlTint:[NSColor currentControlTint]]; - } -} - - (void)initializeUIStyleForDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; - NSColor *secondaryTextColor = [SquirrelPanel secondaryTextColor]; - NSColor *accentColor = [SquirrelPanel accentColor]; NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) @@ -1374,7 +1335,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { highlightedAttrs[NSWritingDirectionAttributeName] = @[@(0)]; NSMutableDictionary *labelAttrs = [attrs mutableCopy]; - labelAttrs[NSForegroundColorAttributeName] = accentColor; + labelAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; labelAttrs[NSFontAttributeName] = userMonoFont; NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; @@ -1382,7 +1343,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { labelHighlightedAttrs[NSFontAttributeName] = userMonoFont; NSMutableDictionary *commentAttrs = [defaultAttrs mutableCopy]; - commentAttrs[NSForegroundColorAttributeName] = secondaryTextColor; + commentAttrs[NSForegroundColorAttributeName] = [NSColor secondaryLabelColor]; commentAttrs[NSFontAttributeName] = userFont; NSMutableDictionary *commentHighlightedAttrs = [defaultAttrs mutableCopy]; @@ -1400,7 +1361,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { preeditHighlightedAttrs[NSLigatureAttributeName] = @(0); NSMutableDictionary *pagingAttrs = [defaultAttrs mutableCopy]; - pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? accentColor : [NSColor controlTextColor]; + pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor controlAccentColor] : [NSColor controlTextColor]; NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear @@ -1466,23 +1427,19 @@ - (instancetype)init { self.backgroundColor = [NSColor clearColor]; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.bounds]; - if (@available(macOS 10.14, *)) { - _back = [[NSVisualEffectView alloc] init]; - _back.blendingMode = NSVisualEffectBlendingModeBehindWindow; - _back.material = NSVisualEffectMaterialHUDWindow; - _back.state = NSVisualEffectStateActive; - _back.wantsLayer = YES; - _back.layer.mask = _view.shape; - [contentView addSubview:_back]; - } + _back = [[NSVisualEffectView alloc] init]; + _back.blendingMode = NSVisualEffectBlendingModeBehindWindow; + _back.material = NSVisualEffectMaterialHUDWindow; + _back.state = NSVisualEffectStateActive; + _back.wantsLayer = YES; + _back.layer.mask = _view.shape; + [contentView addSubview:_back]; [contentView addSubview:_view]; [contentView addSubview:_view.textView]; self.contentView = contentView; [self initializeUIStyleForDarkMode:NO]; - if (@available(macOS 10.14, *)) { - [self initializeUIStyleForDarkMode:YES]; - } + [self initializeUIStyleForDarkMode:YES]; _maxSize = NSZeroSize; _initPosition = YES; } @@ -1621,12 +1578,10 @@ - (void)getTextWidthLimit { // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { - if (@available(macOS 10.14, *)) { - NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: - (_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; - if (self.appearance != requestedAppearance) { - self.appearance = requestedAppearance; - } + NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: + (_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; + if (self.appearance != requestedAppearance) { + self.appearance = requestedAppearance; } //Break line if the text is too long, based on screen size. @@ -1752,14 +1707,12 @@ - (void)show { [_view setFrame:frameRect]; [_view.textView setFrame:textFrameRect]; - if (@available(macOS 10.14, *)) { - if (theme.translucency > 0) { - [_back setFrame:frameRect]; - [_back setAppearance:NSApp.effectiveAppearance]; - [_back setHidden:NO]; - } else { - [_back setHidden:YES]; - } + if (theme.translucency > 0) { + [_back setFrame:frameRect]; + [_back setAppearance:NSApp.effectiveAppearance]; + [_back setHidden:NO]; + } else { + [_back setHidden:YES]; } [self setAlphaValue:theme.alpha]; [self orderFront:nil]; @@ -1805,19 +1758,16 @@ - (void)setLayoutForRange:(NSRange)charRange } else { NSLayoutManager *layoutManager = _view.textView.layoutManager; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - NSUInteger i = glyphRange.location; - NSRange lineRange = NSMakeRange(i, 0); - while (i < NSMaxRange(glyphRange)) { - NSRect rect = [layoutManager lineFragmentRectForGlyphAtIndex:i effectiveRange:&lineRange]; - NSRect usedRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:i effectiveRange:NULL]; + [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange + usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight / 2 + refFont.xHeight / 2); // typesetting glyphs - NSUInteger j = lineRange.location; - while (j < NSMaxRange(lineRange)) { + NSUInteger j = glyphRange.location; + while (j < NSMaxRange(glyphRange)) { NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; - NSDictionary *attrs = [_view.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; + NSDictionary *attrs = [layoutManager.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; NSFont *runFont = attrs[NSFontAttributeName]; NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; @@ -1849,8 +1799,7 @@ - (void)setLayoutForRange:(NSRange)charRange [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); } - i = NSMaxRange(lineRange); - } + }]; } } @@ -2341,10 +2290,8 @@ static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, - (void)loadLabelConfig:(SquirrelConfig *)config { SquirrelTheme *theme = [_view selectTheme:NO]; [SquirrelPanel updateTheme:theme withLabelConfig:config]; - if (@available(macOS 10.14, *)) { - SquirrelTheme *darkTheme = [_view selectTheme:YES]; - [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; - } + SquirrelTheme *darkTheme = [_view selectTheme:YES]; + [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; } + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config { @@ -2446,9 +2393,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config s // get color scheme and then check possible overrides from styleSwitcher for (NSString *prefix in configPrefixes) { // CHROMATICS override - if (@available(macOS 10.12, *)) { - config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; - } + config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backgroundColor; backgroundImage = [config getPattern:[prefix stringByAppendingString:@"/back_image"]] ? : backgroundImage; borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; @@ -2605,27 +2550,22 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config s pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); // CHROMATICS refinement - NSColor *secondaryTextColor = [SquirrelPanel secondaryTextColor]; - NSColor *accentColor = [SquirrelPanel accentColor]; - - if (@available(macOS 10.14, *)) { - if (theme.translucency > 0 && - ((backgroundColor.brightnessComponent >= 0.5 && isDark) || - (backgroundColor.brightnessComponent < 0.5 && !isDark))) { - backgroundColor = inverseColor(backgroundColor); - borderColor = inverseColor(borderColor); - preeditBackgroundColor = inverseColor(preeditBackgroundColor); - candidateTextColor = inverseColor(candidateTextColor); - highlightedCandidateTextColor = [inverseColor(highlightedCandidateTextColor) highlightWithLevel:highlightedCandidateTextColor.brightnessComponent]; - highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1 - highlightedCandidateBackColor.brightnessComponent]; - candidateLabelColor = inverseColor(candidateLabelColor); - highlightedCandidateLabelColor = [inverseColor(highlightedCandidateLabelColor) highlightWithLevel:highlightedCandidateLabelColor.brightnessComponent]; - commentTextColor = inverseColor(commentTextColor); - highlightedCommentTextColor = [inverseColor(highlightedCommentTextColor) highlightWithLevel:highlightedCommentTextColor.brightnessComponent]; - textColor = inverseColor(textColor); - highlightedTextColor = [inverseColor(highlightedTextColor) highlightWithLevel:highlightedTextColor.brightnessComponent]; - highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1 - highlightedBackColor.brightnessComponent]; - } + if (theme.translucency > 0 && + ((backgroundColor.brightnessComponent >= 0.5 && isDark) || + (backgroundColor.brightnessComponent < 0.5 && !isDark))) { + backgroundColor = inverseColor(backgroundColor); + borderColor = inverseColor(borderColor); + preeditBackgroundColor = inverseColor(preeditBackgroundColor); + candidateTextColor = inverseColor(candidateTextColor); + highlightedCandidateTextColor = [inverseColor(highlightedCandidateTextColor) highlightWithLevel:highlightedCandidateTextColor.brightnessComponent]; + highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1 - highlightedCandidateBackColor.brightnessComponent]; + candidateLabelColor = inverseColor(candidateLabelColor); + highlightedCandidateLabelColor = [inverseColor(highlightedCandidateLabelColor) highlightWithLevel:highlightedCandidateLabelColor.brightnessComponent]; + commentTextColor = inverseColor(commentTextColor); + highlightedCommentTextColor = [inverseColor(highlightedCommentTextColor) highlightWithLevel:highlightedCommentTextColor.brightnessComponent]; + textColor = inverseColor(textColor); + highlightedTextColor = [inverseColor(highlightedTextColor) highlightWithLevel:highlightedTextColor.brightnessComponent]; + highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1 - highlightedBackColor.brightnessComponent]; } backgroundColor = backgroundColor ? : [NSColor controlBackgroundColor]; @@ -2634,9 +2574,9 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config s candidateTextColor = candidateTextColor ? : [NSColor controlTextColor]; highlightedCandidateTextColor = highlightedCandidateTextColor ? : [NSColor selectedMenuItemTextColor]; highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? [NSColor alternateSelectedControlColor] : nil; - candidateLabelColor = candidateLabelColor ? : isNative ? accentColor : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); + candidateLabelColor = candidateLabelColor ? : isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); - commentTextColor = commentTextColor ? : secondaryTextColor; + commentTextColor = commentTextColor ? : [NSColor secondaryLabelColor]; highlightedCommentTextColor = highlightedCommentTextColor ? : [NSColor alternateSelectedControlTextColor]; textColor = textColor ? textColor : [NSColor textColor]; highlightedTextColor = highlightedTextColor ? : [NSColor selectedTextColor]; diff --git a/librime b/librime index 3062c63db..e69533de7 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 3062c63db487c1b83083f46a93d588db4b3c3ceb +Subproject commit e69533de741d838340dc3f253a6776229f48e70b diff --git a/main.m b/main.m index fba429e0b..275778ecf 100644 --- a/main.m +++ b/main.m @@ -80,12 +80,8 @@ int main(int argc, char *argv[]) { NSLog(@"Problematic launch detected!"); NSArray *args = @[@"-v", NSLocalizedString(@"say_voice", nil), NSLocalizedString(@"problematic_launch", nil)]; - if (@available(macOS 10.13, *)) { - [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:@"/usr/bin/say"] - arguments:args error:nil terminationHandler:nil]; - } else { - [NSTask launchedTaskWithLaunchPath:@"/usr/bin/say" arguments:args]; - } + [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:@"/usr/bin/say"] + arguments:args error:nil terminationHandler:nil]; } else { [NSApp.squirrelAppDelegate setupRime]; [NSApp.squirrelAppDelegate startRimeWithFullCheck:NO]; From c39d7f5eb3c0472211b984408c1b4f8cb7b82eb2 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 20 Oct 2023 16:13:51 +0200 Subject: [PATCH 146/164] ruby --- SquirrelPanel.m | 573 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 415 insertions(+), 158 deletions(-) diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 1e8ed997f..d8b4d4342 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,8 +3,16 @@ #import "SquirrelConfig.h" #import +static const CGFloat kOffsetHeight = 5; +static const CGFloat kDefaultFontSize = 24; +static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; +static const NSTimeInterval kShowStatusDuration = 1.2; +static NSString *kDefaultCandidateFormat = @"%c. %@"; +static NSString *kTipSpecifier = @"%s"; +static NSString *kFullWidthSpace = @" "; + @implementation NSBezierPath (BezierPathQuartzUtilities) -// This method works only in OS X v10.2 and later. + - (CGPathRef)quartzPath { if (@available(macOS 14.0, *)) { return self.CGPath; @@ -59,7 +67,9 @@ - (CGPathRef)quartzPath { @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) - (void)formatMarkDown { - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: + @"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" + options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; NSInteger __block offset = 0; [regex enumerateMatchesInString:self.string options:0 range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { @@ -80,15 +90,15 @@ - (void)formatMarkDown { } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { [self superscriptRange:[result rangeAtIndex:5]]; [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(id value, NSRange range, BOOL *stop) { - NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + NSFont *font = [NSFont fontWithDescriptor:[value fontDescriptor] size:[value pointSize] * 7 / 12]; [self addAttribute:NSFontAttributeName value:font range:range]; }]; } else if ([tag isEqualToString:@"~"] || [tag isEqualToString:@""]) { [self subscriptRange:[result rangeAtIndex:5]]; [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(id value, NSRange range, BOOL *stop) { - NSFont *font = [[NSFontManager sharedFontManager] convertFont:value toSize:[value pointSize] * 7 / 12]; + usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + NSFont *font = [NSFont fontWithDescriptor:[value fontDescriptor] size:[value pointSize] * 7 / 12]; [self addAttribute:NSFontAttributeName value:font range:range]; }]; } @@ -96,20 +106,81 @@ - (void)formatMarkDown { [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:6].length + [result rangeAtIndex:1].length; }]; - if (offset != 0) { // no match. text remain unchanged. + if (offset != 0) { // repeat until no more nested markdown [self formatMarkDown]; } } -@end +- (CGFloat)annotateRubyInRange:(NSRange)range + verticalLayout:(BOOL)isVertical + maximumLength:(CGFloat)maxLength { + NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: + @"(\uFFF9\\s*)(\\S+?)(\\s*\uFFFA(.+?)\uFFFB)" options:0 error:nil]; + CGFloat __block rubyLineHeight = 0.0; + NSInteger __block offset = 0; + [regex enumerateMatchesInString:self.mutableString options:0 range:range + usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + result = [result resultByAdjustingRangesWithOffset:offset]; + NSRange baseRange = [result rangeAtIndex:2]; + // no ruby annotation if the base string includes line breaks + if ([self attributedSubstringFromRange:NSMakeRange(0, NSMaxRange(baseRange))].size.width > maxLength) { + [self deleteCharactersInRange:NSMakeRange(NSMaxRange([result range]) - 1, 1)]; + [self deleteCharactersInRange:NSMakeRange([result rangeAtIndex:3].location, 1)]; + [self deleteCharactersInRange:NSMakeRange([result rangeAtIndex:1].location, 1)]; + offset -= 3; + } else { + // base string must use only one font so that all fall within one glyph run and the ruby annotation is aligned with no duplicates + NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:baseRange.location effectiveRange:NULL]; + baseFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)baseFont, (CFStringRef)self.string, + CFRangeMake(baseRange.location, baseRange.length))); + [self addAttribute:NSFontAttributeName value:baseFont range:baseRange]; + + CGFloat rubyScale = 0.5; + CFStringRef rubyString = (__bridge CFStringRef)[self.string substringWithRange:[result rangeAtIndex:4]]; + NSFont *rubyFont = [self attribute:NSFontAttributeName atIndex:[result rangeAtIndex:4].location effectiveRange:NULL]; + rubyFont = [NSFont fontWithDescriptor:rubyFont.fontDescriptor size:rubyFont.pointSize * rubyScale]; + rubyFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)rubyFont, rubyString, + CFRangeMake(0, CFStringGetLength(rubyString)))); + rubyLineHeight = MAX(rubyLineHeight, isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender : rubyFont.ascender - rubyFont.descender); + CGColorRef rubyColor = [[self attribute:NSForegroundColorAttributeName + atIndex:[result rangeAtIndex:4].location effectiveRange:NULL] CGColor]; + CGFloat rubyBaselineOffset; + if (@available(macOS 12.0, *)) { + rubyBaselineOffset = isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender : -rubyFont.descender; + } else { + rubyBaselineOffset = isVertical ? rubyFont.verticalFont.ascender : -rubyFont.descender; + } + CFTypeRef keys[] = {kCTFontAttributeName, kCTForegroundColorAttributeName, + kCTBaselineOffsetAttributeName, kCTRubyAnnotationSizeFactorAttributeName, + kCTRubyAnnotationScaleToFitAttributeName}; + CFTypeRef values[] = {(__bridge CTFontRef)rubyFont, rubyColor, + CFNumberCreate(NULL, kCFNumberDoubleType, &rubyBaselineOffset), + CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), kCFBooleanFalse}; + CFDictionaryRef rubyAttrs = CFDictionaryCreate(NULL, keys, values, 5, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, kCTRubyOverhangAuto, kCTRubyPositionBefore, rubyString, rubyAttrs); + + [self deleteCharactersInRange:[result rangeAtIndex:3]]; + if (@available(macOS 12.0, *)) { + [self addAttributes:@{CFBridgingRelease(kCTRubyAnnotationAttributeName): CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName: @(isVertical)} range:baseRange]; + [self deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:3].length + [result rangeAtIndex:1].length; + } else { + // use U+008B as placeholder for line-forward spaces in case ruby is wider than base + [self replaceCharactersInRange:NSMakeRange(NSMaxRange(baseRange), 0) withString:[NSString stringWithFormat:@"%C", 0x8B]]; + baseRange.length += 1; + [self addAttributes:@{CFBridgingRelease(kCTRubyAnnotationAttributeName): CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName: @(isVertical)} range:baseRange]; + [self deleteCharactersInRange:[result rangeAtIndex:1]]; + offset -= [result rangeAtIndex:3].length - 1 + [result rangeAtIndex:1].length; + } + } + }]; + return rubyLineHeight; +} -static const CGFloat kOffsetHeight = 5; -static const CGFloat kDefaultFontSize = 24; -static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; -static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *const kDefaultCandidateFormat = @"%c. %@"; -static NSString *const kTipSpecifier = @"%s"; -static NSString *const kFullWidthSpace = @" "; +@end @interface SquirrelTheme : NSObject @@ -153,6 +224,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSParagraphStyle *pagingParagraphStyle; @property(nonatomic, strong, readonly) NSParagraphStyle *statusParagraphStyle; +@property(nonatomic, strong, readonly) NSAttributedString *separator; @property(nonatomic, strong, readonly) NSAttributedString *symbolBackFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolBackStroke; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; @@ -210,6 +282,8 @@ - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)setStatusMessageType:(NSString *)statusMessageType; +- (void)setAnnotationHeight:(CGFloat)height; + @end @implementation SquirrelTheme @@ -369,37 +443,54 @@ - (void) setAttrs:(NSDictionary *)attrs _pagingAttrs = pagingAttrs; _pagingHighlightedAttrs = pagingHighlightedAttrs; _statusAttrs = statusAttrs; + + NSMutableDictionary *sepAttrs = [commentAttrs mutableCopy]; + sepAttrs[NSVerticalGlyphFormAttributeName] = @(NO); + _separator = [[NSAttributedString alloc] initWithString:_linear ? (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:sepAttrs]; + NSMutableDictionary *symbolAttrs = [pagingAttrs mutableCopy]; if (@available(macOS 12.0, *)) { - NSTextAttachment *attmBackFill = [[NSTextAttachment alloc] init]; - attmBackFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.backward.circle.fill" accessibilityDescription:nil]; + NSTextAttachment *attmLeftFill = [[NSTextAttachment alloc] init]; + attmLeftFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.left.circle.fill" accessibilityDescription:nil]; + NSTextAttachment *attmLeftStroke = [[NSTextAttachment alloc] init]; + attmLeftStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.left.circle" accessibilityDescription:nil]; + NSTextAttachment *attmRightFill = [[NSTextAttachment alloc] init]; + attmRightFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.right.circle.fill" accessibilityDescription:nil]; + NSTextAttachment *attmRightStroke = [[NSTextAttachment alloc] init]; + attmRightStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.right.circle" accessibilityDescription:nil]; + NSTextAttachment *attmUpFill = [[NSTextAttachment alloc] init]; + attmUpFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.up.circle.fill" accessibilityDescription:nil]; + NSTextAttachment *attmUpStroke = [[NSTextAttachment alloc] init]; + attmUpStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.up.circle" accessibilityDescription:nil]; + NSTextAttachment *attmDownFill = [[NSTextAttachment alloc] init]; + attmDownFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.down.circle.fill" accessibilityDescription:nil]; + NSTextAttachment *attmDownStroke = [[NSTextAttachment alloc] init]; + attmDownStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.down.circle" accessibilityDescription:nil]; + NSMutableDictionary *attrsBackFill = [symbolAttrs mutableCopy]; - attrsBackFill[NSAttachmentAttributeName] = attmBackFill; + attrsBackFill[NSAttachmentAttributeName] = _linear ? attmUpFill : attmLeftFill; _symbolBackFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackFill]; - - NSTextAttachment *attmBackStroke = [[NSTextAttachment alloc] init]; - attmBackStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.backward.circle" accessibilityDescription:nil]; NSMutableDictionary *attrsBackStroke = [symbolAttrs mutableCopy]; - attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; + attrsBackStroke[NSAttachmentAttributeName] = _linear ? attmUpStroke : attmLeftStroke; _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackStroke]; - - NSTextAttachment *attmForwardFill = [[NSTextAttachment alloc] init]; - attmForwardFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.forward.circle.fill" accessibilityDescription:nil]; NSMutableDictionary *attrsForwardFill = [symbolAttrs mutableCopy]; - attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; + attrsForwardFill[NSAttachmentAttributeName] = _linear ? attmDownFill : attmRightFill; _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardFill]; - - NSTextAttachment *attmForwardStroke = [[NSTextAttachment alloc] init]; - attmForwardStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.forward.circle" accessibilityDescription:nil]; NSMutableDictionary *attrsForwardStroke = [symbolAttrs mutableCopy]; - attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; + attrsForwardStroke[NSAttachmentAttributeName] = _linear ? attmDownStroke : attmRightStroke; _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardStroke]; } else { NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] size:[labelAttrs[NSFontAttributeName] pointSize]]; - symbolAttrs[NSFontAttributeName] = symbolFont; - if (_vertical || !_linear) { + if (_linear) { + CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2); + CTFontRef rotatedSymbolFont = CTFontCreateCopyWithSymbolicTraits((CTFontRef)symbolFont, symbolFont.pointSize, &transform, kCTFontTraitVertical, kCTFontTraitClassMask); + symbolAttrs[NSFontAttributeName] = CFBridgingRelease(rotatedSymbolFont); + symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] + symbolFont.ascender); + symbolAttrs[CFBridgingRelease(kCTTrackingAttributeName)] = @(symbolFont.ascender); + } else { + symbolAttrs[NSFontAttributeName] = symbolFont; symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] - symbolFont.leading); } NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; @@ -411,20 +502,8 @@ - (void) setAttrs:(NSDictionary *)attrs symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE93 forFont:symbolFont baseString:@"▶"]; //gid4967 symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE94 forFont:symbolFont baseString:@"▷"]; //gid4968 _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; - - NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; - symbolAttrsBackStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4969" forFont:symbolFont baseString:@"◁"]; _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; - - NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; - symbolAttrsForwardFill[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4967" forFont:symbolFont baseString:@"▶"]; _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" attributes:symbolAttrsForwardFill]; - - NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; - symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = - [NSGlyphInfo glyphInfoWithGlyphName:@"gid4968" forFont:symbolFont baseString:@"▷"]; _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" attributes:symbolAttrsForwardStroke]; } } @@ -494,6 +573,130 @@ - (void)setStatusMessageType:(NSString *)type { } } +- (void)setAnnotationHeight:(CGFloat)height { + if (height > 0 && _linespace < height * 2) { + _linespace = height * 2; + NSMutableParagraphStyle *paragraphStyle = [_paragraphStyle mutableCopy]; + paragraphStyle.paragraphSpacingBefore = height; + paragraphStyle.paragraphSpacing = height; + _paragraphStyle = paragraphStyle; + } +} + +@end + +@interface SquirrelLayoutManager : NSLayoutManager +@end + +@implementation SquirrelLayoutManager + +- (void)drawGlyphsForGlyphRange:(NSRange)glyphRange + atPoint:(NSPoint)origin { + NSRange charRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + NSTextContainer *textContainer = [self textContainerForGlyphAtIndex:glyphRange.location effectiveRange:NULL withoutAdditionalLayout:YES]; + CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; + CGContextResetClip(context); + [self.textStorage enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSRange glyRange = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + if (attrs[CFBridgingRelease(kCTRubyAnnotationAttributeName)]) { + CGContextSaveGState(context); + CGContextScaleCTM(context, 1.0, -1.0); + NSUInteger glyphIndex = glyRange.location; + NSRect lineRect = [self lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL withoutAdditionalLayout:YES]; + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) + [self.textStorage attributedSubstringFromRange:range]); + CFArrayRef runs = CTLineGetGlyphRuns(line); + for (CFIndex i = 0; i < CFArrayGetCount(runs); ++i) { + CGPoint position = [self locationForGlyphAtIndex:glyphIndex]; + CTRunRef run = CFArrayGetValueAtIndex(runs, i); + CGAffineTransform matrix = CTRunGetTextMatrix(run); + matrix.tx = origin.x + NSMinX(lineRect) + position.x; + matrix.ty = - origin.y - NSMinY(lineRect) - position.y; + CGContextSetTextMatrix(context, matrix); + CTRunDraw(run, context, CFRangeMake(0, 0)); + glyphIndex += CTRunGetGlyphCount(run); + } + CGContextRestoreGState(context); + CFRelease(line); + } else { + [super drawGlyphsForGlyphRange:glyRange atPoint:origin]; + } + }]; + CGContextClipToRect(context, textContainer.textView.superview.bounds); +} + +- (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager + shouldUseAction:(NSControlCharacterAction)action + forControlCharacterAtIndex:(NSUInteger)charIndex { + if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B && + [layoutManager.textStorage attribute:CFBridgingRelease(kCTRubyAnnotationAttributeName) atIndex:charIndex effectiveRange:NULL]) { + return NSControlCharacterActionWhitespace; + } else { + return action; + } +} + +- (NSRect) layoutManager:(NSLayoutManager *)layoutManager + boundingBoxForControlGlyphAtIndex:(NSUInteger)glyphIndex + forTextContainer:(NSTextContainer *)textContainer + proposedLineFragment:(NSRect)proposedRect + glyphPosition:(NSPoint)glyphPosition + characterIndex:(NSUInteger)charIndex { + CGFloat width = 0.0; + if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B) { + NSRange rubyRange; + id rubyAnnotation = [layoutManager.textStorage attribute:CFBridgingRelease(kCTRubyAnnotationAttributeName) atIndex:charIndex effectiveRange:&rubyRange]; + if (rubyAnnotation) { + NSAttributedString *rubyString = [layoutManager.textStorage attributedSubstringFromRange:rubyRange]; + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)rubyString); + CGRect rubyRect = CTLineGetBoundsWithOptions(line, 0); + CFRelease(line); + NSSize baseSize = rubyString.size; + width = MAX(0.0, rubyRect.size.width - baseSize.width); + } + } + return NSMakeRect(glyphPosition.x, 0.0, width, glyphPosition.y); +} + +@end + +API_AVAILABLE(macos(12.0)) +@interface SquirrelTextLayoutFragment : NSTextLayoutFragment +@end + +@implementation SquirrelTextLayoutFragment + +- (void)drawAtPoint:(CGPoint)point + inContext:(CGContextRef)context { + BOOL isVertical = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + NSArray *lineFragments = self.textLineFragments; + for (NSTextLineFragment *lineFrag in lineFragments) { + CGRect lineRect = lineFrag.typographicBounds; + NSFont *refFont = [lineFrag.attributedString attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; + NSParagraphStyle *rulerStyle = [lineFrag.attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; + CGFloat lineHeight = rulerStyle.minimumLineHeight; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGPoint renderOrigin = CGPointMake(point.x + NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, + point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y - refFontHeight / 2 + (isVertical ? 0.0 : lineHeight / 2 + refFont.descender)); + [lineFrag drawAtPoint:renderOrigin inContext:context]; + } +} + +@end + +API_AVAILABLE(macos(12.0)) +@interface SquirrelTextLayoutManager : NSTextLayoutManager +@end + +@implementation SquirrelTextLayoutManager + +- (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutManager + textLayoutFragmentForLocation:(id)location + inTextElement:(NSTextElement *)textElement { + return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement range:textElement.elementRange]; +} + @end @interface SquirrelView : NSView @@ -569,8 +772,9 @@ - (instancetype)initWithFrame:(NSRect)frameRect { } if (@available(macOS 12.0, *)) { - NSTextLayoutManager *textLayoutManager = [[NSTextLayoutManager alloc] init]; + SquirrelTextLayoutManager *textLayoutManager = [[SquirrelTextLayoutManager alloc] init]; textLayoutManager.usesFontLeading = NO; + textLayoutManager.delegate = textLayoutManager; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; textContainer.lineFragmentPadding = 0; @@ -581,10 +785,11 @@ - (instancetype)initWithFrame:(NSRect)frameRect { textContainer:textLayoutManager.textContainer]; _textStorage = _textView.textContentStorage.textStorage; } else { - NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; + SquirrelLayoutManager *layoutManager = [[SquirrelLayoutManager alloc] init]; layoutManager.backgroundLayoutEnabled = YES; layoutManager.usesFontLeading = NO; layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; + layoutManager.delegate = layoutManager; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; textContainer.lineFragmentPadding = 0; @@ -605,13 +810,13 @@ - (instancetype)initWithFrame:(NSRect)frameRect { return self; } -- (NSTextRange *)getTextRangeFromRange:(NSRange)range API_AVAILABLE(macos(12.0)) { - if (range.location == NSNotFound) { +- (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(macos(12.0)) { + if (charRange.location == NSNotFound) { return nil; } else { NSTextContentStorage *contentStorage = _textView.textContentStorage; - id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location withOffset:range.location]; - id endLocation = [contentStorage locationFromLocation:startLocation withOffset:range.length]; + id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location withOffset:charRange.location]; + id endLocation = [contentStorage locationFromLocation:startLocation withOffset:charRange.length]; return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; } } @@ -630,7 +835,7 @@ - (NSRect)contentRect { // Get the rectangle containing the range of text, will first convert to glyph or text range, expensive to calculate - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { - NSTextRange *textRange = [self getTextRangeFromRange:range]; + NSTextRange *textRange = [self getTextRangeFromCharRange:range]; __block NSRect contentRect = NSZeroRect; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange @@ -748,7 +953,7 @@ static inline BOOL nearEmptyRect(NSRect rect) { // bodyRect is the complete line fragment in the middle if the range spans no less than one full line - (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)leadingRect bodyRect:(NSRectPointer)bodyRect trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { - NSTextRange *textRange = [self getTextRangeFromRange:charRange]; + NSTextRange *textRange = [self getTextRangeFromCharRange:charRange]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:charRange.location effectiveRange:NULL] lineSpacing]; NSMutableArray *lineRects = [[NSMutableArray alloc] initWithCapacity:2]; NSMutableArray *lineRanges = [[NSMutableArray alloc] initWithCapacity:2]; @@ -919,10 +1124,14 @@ - (void)updateLayer { candidateBlockRect.origin.y += theme.linespace / 2; } } - if (!theme.linear && pagingRange.length > 0) { + if (pagingRange.length > 0) { pagingLineRect = [self contentRectForRange:pagingRange]; - pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; - pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; + if (theme.linear) { + pagingLineRect = NSOffsetRect(pagingLineRect, _insets.left, theme.edgeInset.height); + } else { + pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; + pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; + } } [NSBezierPath setDefaultLineWidth:0]; @@ -1034,7 +1243,7 @@ - (void)updateLayer { [candidateVertGridPath closePath]; } CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = pow(2, ceil(log2((tailEdge - leadOrigin.x) / tabInterval))) * tabInterval + leadOrigin.x; + CGFloat tabPosition = ceil((tailEdge - leadOrigin.x) / tabInterval / 2) * tabInterval * 2 + leadOrigin.x; if (i == _candidateRanges.count - 1 && pagingRange.length > 0 && tailEdge < pagingLineRect.origin.x) { tabPosition = MIN(tabPosition, floor((pagingLineRect.origin.x - leadOrigin.x) / tabInterval) * tabInterval + leadOrigin.x); } @@ -1137,10 +1346,10 @@ - (void)updateLayer { if (theme.backgroundImage) { CAShapeLayer *backgroundImageLayer = [[CAShapeLayer alloc] init]; if (theme.vertical) { - const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI / 2); + const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI_2); backgroundImageLayer.path = CFAutorelease(CGPathCreateCopyByTransformingPath([textContainerPath quartzPath], &rotate)); backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; - [backgroundImageLayer setAffineTransform:CGAffineTransformMakeRotation(M_PI / 2)]; + [backgroundImageLayer setAffineTransform:CGAffineTransformInvert(rotate)]; } else { backgroundImageLayer.path = [textContainerPath quartzPath]; backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; @@ -1322,7 +1531,7 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; // prevent mac terminal from hijacking non-alphabetic keys on non-inline mode defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); - + NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; attrs[NSFontAttributeName] = userFont; @@ -1571,7 +1780,7 @@ - (void)getTextWidthLimit { } if (theme.tabled) { CGFloat tabInterval = theme.separatorWidth * 2; - _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; + _textWidthLimit = round((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; } _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX); } @@ -1588,6 +1797,7 @@ - (void)show { SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; NSEdgeInsets insets = _view.insets; + CGFloat offsetHeight = MAX(kOffsetHeight, round(MAX(NSWidth(_position), NSHeight(_position)) / 2)); CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = _screen.visibleFrame; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; @@ -1602,8 +1812,8 @@ - (void)show { } } if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) - : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) + if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + offsetHeight) + : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect)))) && theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { @@ -1613,8 +1823,8 @@ - (void)show { [textContainer setSize:NSMakeSize(_maxSize.width, textHeightLimit)]; } } - if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) - : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : kOffsetHeight))) { + if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? offsetHeight : 0)) + : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : offsetHeight))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { @@ -1626,57 +1836,58 @@ - (void)show { _initPosition |= NSIntersectsRect(self.frame, _position); NSRect windowRect; if (theme.vertical) { - windowRect.size = NSMakeSize(NSHeight(maxContentRect) + insets.top + insets.bottom, - NSWidth(maxContentRect) + insets.left + insets.right); - if (_initPosition ) { + windowRect = NSMakeRect(NSMaxX(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right); + _initPosition |= NSIntersectsRect(windowRect, _position); + if (_initPosition) { // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -kOffsetHeight) - NSHeight(windowRect); + if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + offsetHeight) { + windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -offsetHeight) - NSHeight(windowRect); } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : offsetHeight); } // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSWidth(windowRect); + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? offsetHeight : 0) - NSWidth(windowRect); if (!sweepVertical && _view.preeditRange.length > 0) { NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); } - } else { - windowRect.origin.x = NSMaxX(self.frame) - NSWidth(windowRect); - windowRect.origin.y = NSMaxY(self.frame) - NSHeight(windowRect); } } else { - windowRect.size = NSMakeSize(NSWidth(maxContentRect) + insets.left + insets.right, - NSHeight(maxContentRect) + insets.top + insets.bottom); + windowRect = NSMakeRect(NSMinX(self.frame), + NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom); + _initPosition |= NSIntersectsRect(windowRect, _position); if (_initPosition) { if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) { + windowRect.origin.x = NSMinX(_position) - offsetHeight - NSWidth(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + windowRect.origin.x = NSMaxX(_position) + offsetHeight; } windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, - NSMinY(_position) - kOffsetHeight - NSHeight(windowRect)); + NSMinY(_position) - offsetHeight - NSHeight(windowRect)); } - } else { - windowRect.origin = NSMakePoint(NSMinX(self.frame), NSMaxY(self.frame) - NSHeight(windowRect)); } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (_initPosition && sweepVertical ? NSMinX(_position) - kOffsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? NSMinX(_position) - offsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = _initPosition && sweepVertical ? NSMaxX(_position) + kOffsetHeight : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? NSMaxX(_position) + offsetHeight : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = _initPosition && !sweepVertical ? NSMaxY(_position) + kOffsetHeight : NSMinY(screenRect); + windowRect.origin.y = _initPosition && !sweepVertical ? NSMaxY(_position) + offsetHeight : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (_initPosition && !sweepVertical ? NSMinY(_position) - kOffsetHeight : NSMaxY(screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? NSMinY(_position) - offsetHeight : NSMaxY(screenRect)) - NSHeight(windowRect); } if (theme.vertical) { @@ -1739,31 +1950,38 @@ - (void)setLayoutForRange:(NSRange)charRange style.minimumLineHeight); lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; if (@available(macOS 12.0, *)) { - NSUInteger i = charRange.location; - NSRange runRange = NSMakeRange(i, 0); - while (i < NSMaxRange(charRange)) { - NSDictionary *attrs = [_view.textStorage attributesAtIndex:i - longestEffectiveRange:&runRange inRange:charRange]; - NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; - CGFloat offset = (baselineOffset ? baselineOffset.doubleValue : 0.0) + lineHeight / 2 - refFontHeight / 2; + [_view.textStorage + enumerateAttributesInRange:charRange + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSFont *refFont = attrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)][CFBridgingRelease(kCTBaselineReferenceFont)]; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue] + lineHeight / 2 - refFontHeight / 2; NSNumber *superscript = attrs[NSSuperscriptAttributeName]; if (superscript) { NSFont *runFont = verticalLayout ? [attrs[NSFontAttributeName] verticalFont] : attrs[NSFontAttributeName]; - offset += superscript.integerValue == 1 ? runFont.descender / 3 : runFont.ascender / 3; + baselineOffset += superscript.integerValue == 1 ? runFont.descender / 3 : runFont.ascender / 3; } [_view.textStorage addAttribute:NSBaselineOffsetAttributeName - value:@(offset) range:runRange]; - i = NSMaxRange(runRange); - } + value:@(baselineOffset) range:range]; + }]; } else { NSLayoutManager *layoutManager = _view.textView.layoutManager; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange - usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange glyphRange, BOOL *stop) { - CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : lineHeight / 2 + refFont.xHeight / 2); + usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange range, BOOL *stop) { + NSFont *refFont = [layoutManager.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:range.location effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); // typesetting glyphs - NSUInteger j = glyphRange.location; - while (j < NSMaxRange(glyphRange)) { + NSUInteger j = range.location; + while (j < NSMaxRange(range)) { NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; @@ -1803,11 +2021,11 @@ - (void)setLayoutForRange:(NSRange)charRange } } -- (BOOL)shouldBreakLineWithRange:(NSRange)range { +- (BOOL)shouldBreakLineBeforeRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; + NSUInteger __block lineCount = 0; if (@available(macOS 12.0, *)) { - NSTextRange *textRange = [_view getTextRangeFromRange:range]; - NSUInteger __block lineCount = 0; + NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard @@ -1816,27 +2034,21 @@ - (BOOL)shouldBreakLineWithRange:(NSRange)range { ++lineCount; return YES; }]; - return lineCount > 1; } else { NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSUInteger loc = glyphRange.location; - NSRange lineRange = NSMakeRange(loc, 0); - NSUInteger lineCount = 0; - while (loc < NSMaxRange(glyphRange)) { - [_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:loc - effectiveRange:&lineRange]; + [_view.textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange + usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { ++lineCount; - loc = NSMaxRange(lineRange); - } - return lineCount > 1; + }]; } + return lineCount > 1; } - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLength { [_view.textStorage fixFontAttributeInRange:range]; if (@available(macOS 12.0, *)) { - NSTextRange *textRange = [_view getTextRangeFromRange:range]; + NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; CGFloat __block rangeEdge; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange @@ -1915,10 +2127,6 @@ - (void)showPreedit:(NSString *)preedit if (theme.lineLength > 0) { _maxSize.width = MIN(theme.lineLength, _textWidthLimit); } - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, - theme.edgeInset.width + theme.separatorWidth / 2, - theme.edgeInset.height + theme.linespace / 2, - theme.edgeInset.width + theme.separatorWidth / 2); NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSMutableAttributedString alloc] init]]; @@ -1957,11 +2165,9 @@ - (void)showPreedit:(NSString *)preedit preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; - insets.top = theme.edgeInset.height; if (numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { - insets.bottom = theme.edgeInset.height; goto typesetter; } } @@ -1983,13 +2189,28 @@ - (void)showPreedit:(NSString *)preedit [item replaceCharactersInRange:candidateRange withString:candidates[idx]]; NSRange commentRange = [item.string rangeOfString:kTipSpecifier]; - if (idx < comments.count && [comments[idx] length] != 0) { + if ([comments[idx] length] != 0) { [item replaceCharactersInRange:commentRange withString:[@" " stringByAppendingString:comments[idx]]]; } else { [item deleteCharactersInRange:commentRange]; } [item formatMarkDown]; + CGFloat annotationHeight = [item annotateRubyInRange:NSMakeRange(0, item.length) verticalLayout:theme.vertical maximumLength:_textWidthLimit]; + if (annotationHeight * 2 > theme.linespace) { + [self setAnnotationHeight:annotationHeight]; + paragraphStyleCandidate = [theme.paragraphStyle copy]; + [text enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart) options:0 + usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL *stop) { + NSMutableParagraphStyle *style = [value mutableCopy]; + style.paragraphSpacing = annotationHeight; + style.paragraphSpacingBefore = annotationHeight; + [text addAttribute:NSParagraphStyleAttributeName value:style range:range]; + }]; + } + if ([comments[idx] length] != 0 && [item.string hasSuffix:@" "]) { + [item deleteCharactersInRange:NSMakeRange(item.length - 1, 1)]; + } if (!theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.headIndent = labelWidth; @@ -2001,33 +2222,32 @@ - (void)showPreedit:(NSString *)preedit // determine if the line is too wide and line break is needed, based on screen size. if (lineStart != text.length) { NSUInteger separatorStart = text.length; - NSMutableAttributedString *separator = [[NSMutableAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.commentAttrs]; - if (theme.tabled) { + // separator: linear = " "; tabled = " \t"; stacked = "\n" + NSMutableAttributedString *separator = [theme.separator mutableCopy]; + if (theme.tabled) { // fill gaps to make cells 2^n tabs wide CGFloat widthInTabs = (ceil([text attributedSubstringFromRange:candidateRanges.lastObject.rangeValue].size.width) + theme.separatorWidth) / tabInterval; - NSUInteger numPaddingTabs = pow(2, ceil(log2(widthInTabs))) - ceil(widthInTabs); + NSUInteger numPaddingTabs = ceil(widthInTabs / 2) * 2 - ceil(widthInTabs); [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; } - [separator addAttribute:NSVerticalGlyphFormAttributeName value:@(NO) - range:NSMakeRange(0, separator.length)]; - NSRange separatorRange = NSMakeRange(separatorStart, separator.length); [text appendAttributedString:separator]; [text appendAttributedString:item]; - if (theme.linear && (ceil(item.size.width) > _textWidthLimit || [self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)])) { - [text replaceCharactersInRange:separatorRange withString:theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\n"] : @"\n"]; - lineStart = separatorStart + (theme.tabled ? 2 : 1); + if (theme.linear && (ceil(item.size.width + theme.separatorWidth) > _textWidthLimit || + [self shouldBreakLineBeforeRange:NSMakeRange(lineStart, text.length - lineStart)])) { + [text replaceCharactersInRange:NSMakeRange(separatorStart + 1, separator.length - 1) withString:@"\n"]; + lineStart = separatorStart + 2; } } else { // at the start of a new line, no need to determine line break [text appendAttributedString:item]; } // for linear layout, middle-truncate candidates that are longer than one line - if (theme.linear && ceil(item.size.width) > _textWidthLimit) { + if (theme.linear && ceil(item.size.width + theme.separatorWidth) > _textWidthLimit) { if (idx < numCandidates - 1 || theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; } NSMutableParagraphStyle *paragraphStyleTruncating = [paragraphStyleCandidate mutableCopy]; paragraphStyleTruncating.lineBreakMode = NSLineBreakByTruncatingMiddle; [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleTruncating range:NSMakeRange(lineStart, item.length)]; - [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(lineStart, text.length - lineStart - 1)]]; + [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(lineStart, item.length)]]; lineStart = text.length; } else { [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length - item.length, item.length)]]; @@ -2048,30 +2268,27 @@ - (void)showPreedit:(NSString *)preedit [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 1, 1)]; } - [text appendAttributedString:[[NSAttributedString alloc] initWithString:theme.linear ? (theme.tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:theme.commentAttrs]]; + [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; CGFloat maxLineLength; [text appendAttributedString:paging]; if (theme.linear) { - if ([self shouldBreakLineWithRange:NSMakeRange(lineStart, text.length - lineStart)]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:theme.tabled ? @"\n\t" : [@"\n" stringByAppendingString:kFullWidthSpace]]; + if ([self shouldBreakLineBeforeRange:NSMakeRange(lineStart, text.length - lineStart)]) { + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 0) withString:@"\n"]; lineStart = pagingStart; pagingStart += 1; } if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; - if (theme.tabled) { - maxLineLength = ceil(maxLineLength / tabInterval) * tabInterval - theme.separatorWidth; - } else { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; - } + paragraphStyleCandidate.tabStops = @[]; CGFloat candidateEndPosition = ceil([text attributedSubstringFromRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)].size.width); - NSMutableArray *tabStops = [[NSMutableArray alloc] init]; - for (NSUInteger i = 1; tabInterval * i < candidateEndPosition; ++i) { - [tabStops addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:tabInterval * i]]; + CGFloat textPostion = tabInterval; + while (textPostion < candidateEndPosition) { + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:textPostion]]; + textPostion += tabInterval; } - [tabStops addObject:[[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; - paragraphStyleCandidate.tabStops = tabStops; + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithType:NSRightTabStopType location:_textWidthLimit]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate @@ -2087,28 +2304,37 @@ - (void)showPreedit:(NSString *)preedit [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging range:NSMakeRange(pagingStart, paging.length)]; - insets.bottom = theme.edgeInset.height; } pagingRange = NSMakeRange(text.length - paging.length, paging.length); } typesetter: [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, + theme.edgeInset.width + theme.separatorWidth / 2, + theme.edgeInset.height + theme.linespace / 2, + theme.edgeInset.width + theme.separatorWidth / 2); if (preedit) { [self setLayoutForRange:preeditRange withReferenceFont:(theme.vertical ? [theme.preeditAttrs[NSFontAttributeName] verticalFont] : theme.preeditAttrs[NSFontAttributeName]) paragraphStyle:theme.preeditParagraphStyle]; + insets.top = theme.edgeInset.height; } if (numCandidates > 0) { NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); + NSFont *refFont = getTallestFont(@[theme.attrs[NSFontAttributeName], theme.labelAttrs[NSFontAttributeName], + theme.commentAttrs[NSFontAttributeName]], theme.vertical); [self setLayoutForRange:candidateBlockRange - withReferenceFont:(theme.vertical ? [theme.attrs[NSFontAttributeName] verticalFont] : theme.attrs[NSFontAttributeName]) + withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) paragraphStyle:theme.paragraphStyle]; if (!theme.linear && pagingRange.length > 0) { [self setLayoutForRange:pagingRange withReferenceFont:theme.pagingAttrs[NSFontAttributeName] paragraphStyle:theme.pagingParagraphStyle]; + insets.bottom = theme.edgeInset.height; } + } else { + insets.bottom = theme.edgeInset.height; } // text done! @@ -2124,7 +2350,8 @@ - (void)showPreedit:(NSString *)preedit [self show]; } -- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { +- (void)updateStatusLong:(NSString *)messageLong + statusShort:(NSString *)messageShort { SquirrelTheme *theme = _view.currentTheme; if ([theme.statusMessageType isEqualToString:@"mix"]) { if (messageShort) { @@ -2251,6 +2478,19 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return lineHeight; } +static NSFont * getTallestFont(NSArray*fonts, BOOL vertical) { + NSFont *tallestFont; + CGFloat maxHeight = 0.0; + for (NSFont *font in fonts) { + CGFloat fontHeight = getLineHeight(font, vertical); + if (fontHeight > maxHeight) { + tallestFont = font; + maxHeight = fontHeight; + } + } + return tallestFont; +} + static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, SquirrelConfig *config, NSString *prefix) { NSString *candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { @@ -2287,6 +2527,11 @@ static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, } } +- (void)setAnnotationHeight:(CGFloat)height { + [[_view selectTheme:NO] setAnnotationHeight:height]; + [[_view selectTheme:YES] setAnnotationHeight:height]; +} + - (void)loadLabelConfig:(SquirrelConfig *)config { SquirrelTheme *theme = [_view selectTheme:NO]; [SquirrelPanel updateTheme:theme withLabelConfig:config]; @@ -2294,7 +2539,8 @@ - (void)loadLabelConfig:(SquirrelConfig *)config { [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; } -+ (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config { ++ (void)updateTheme:(SquirrelTheme *)theme + withLabelConfig:(SquirrelConfig *)config { int menuSize = [config getInt:@"menu/page_size"] ? : 5; NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; @@ -2320,13 +2566,17 @@ + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)con [theme setLabels:labels]; } -- (void)loadConfig:(SquirrelConfig *)config forDarkMode:(BOOL)isDark { +- (void)loadConfig:(SquirrelConfig *)config + forDarkMode:(BOOL)isDark { SquirrelTheme *theme = [_view selectTheme:isDark]; NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forDarkMode:isDark]; } -+ (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config styleOptions:(NSSet *)styleOptions forDarkMode:(BOOL)isDark { ++ (void)updateTheme:(SquirrelTheme *)theme + withConfig:(SquirrelConfig *)config + styleOptions:(NSSet *)styleOptions + forDarkMode:(BOOL)isDark { // INTERFACE BOOL linear = NO; BOOL tabled = NO; @@ -2524,11 +2774,20 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config s pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *refFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)font, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *refFont = getTallestFont(@[font, labelFont, commentFont], vertical); + refFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)refFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); labelAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); labelHighlightedAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); - labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; - labelHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): refFont}; + labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + labelHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + attrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + highlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + commentAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + commentHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + pagingAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): linear ? labelFont : pagingFont}; + statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? commentFont.verticalFont : commentFont}; attrs[NSBaselineOffsetAttributeName] = baseOffset; highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; @@ -2550,9 +2809,7 @@ + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config s pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); // CHROMATICS refinement - if (theme.translucency > 0 && - ((backgroundColor.brightnessComponent >= 0.5 && isDark) || - (backgroundColor.brightnessComponent < 0.5 && !isDark))) { + if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - isDark) <= 0.4) { backgroundColor = inverseColor(backgroundColor); borderColor = inverseColor(borderColor); preeditBackgroundColor = inverseColor(preeditBackgroundColor); From 5798e0a2e6ba21d2e5db797735e9fe9bcb92fd74 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 4 Nov 2023 19:29:21 +0100 Subject: [PATCH 147/164] icon --- Assets.xcassets/rime.imageset/Contents.json | 19 ++- Assets.xcassets/rime.imageset/rime.svg | 19 +-- Info.plist | 31 ++--- Squirrel.xcodeproj/project.pbxproj | 122 +++++++++++--------- librime | 2 +- 5 files changed, 104 insertions(+), 89 deletions(-) diff --git a/Assets.xcassets/rime.imageset/Contents.json b/Assets.xcassets/rime.imageset/Contents.json index f980ff3d6..7e46fb00c 100644 --- a/Assets.xcassets/rime.imageset/Contents.json +++ b/Assets.xcassets/rime.imageset/Contents.json @@ -1,8 +1,23 @@ { "images" : [ { + "compression-type" : "lossless", "filename" : "rime.svg", - "idiom" : "universal" + "idiom" : "mac", + "resizing" : { + "cap-insets" : { + "bottom" : 2, + "left" : 5, + "right" : 5, + "top" : 2 + }, + "center" : { + "height" : 12, + "mode" : "tile", + "width" : 12 + }, + "mode" : "9-part" + } } ], "info" : { @@ -10,6 +25,8 @@ "version" : 1 }, "properties" : { + "compression-type" : "lossless", + "preserves-vector-representation" : true, "template-rendering-intent" : "template" } } diff --git a/Assets.xcassets/rime.imageset/rime.svg b/Assets.xcassets/rime.imageset/rime.svg index b1a4cf61d..a7126197c 100644 --- a/Assets.xcassets/rime.imageset/rime.svg +++ b/Assets.xcassets/rime.imageset/rime.svg @@ -1,13 +1,14 @@ - + - - - - - + viewBox="0 0 22 16" style="enable-background:new 0 0 22 16;" xml:space="preserve"> + + + diff --git a/Info.plist b/Info.plist index fec7d1ba2..f09819368 100644 --- a/Info.plist +++ b/Info.plist @@ -26,11 +26,6 @@ im.rime.inputmethod.Squirrel.Hant - TISIconLabels - - Primary - - TISInputSourceID im.rime.inputmethod.Squirrel.Hant TISIntendedLanguage @@ -49,9 +44,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.imageset + rime tsInputModeAlternateMenuIconFileKey - rime.imageset + rime tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -59,11 +54,6 @@ im.rime.inputmethod.Squirrel.Hans - TISIconLabels - - Primary - - TISInputSourceID im.rime.inputmethod.Squirrel.Hans TISIntendedLanguage @@ -82,9 +72,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.imageset + rime tsInputModeAlternateMenuIconFileKey - rime.imageset + rime tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -92,11 +82,6 @@ im.rime.inputmethod.Squirrel.Cant - TISIconLabels - - Primary - - TISInputSourceID im.rime.inputmethod.Squirrel.Cant TISIntendedLanguage @@ -115,9 +100,9 @@ tsInputModeKeyEquivalentModifiersKey 4608 tsInputModeMenuIconFileKey - rime.imageset + rime tsInputModeAlternateMenuIconFileKey - rime.imageset + rime tsInputModePrimaryInScriptKey tsInputModeScriptKey @@ -158,8 +143,8 @@ TISIconIsTemplate tsInputMethodIconFileKey - rime.imageset + rime tsInputMethodAlternateMenuIconFileKey - rime.imageset + rime diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 04b36e8e0..742804b2d 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -72,30 +72,32 @@ 7BDB21231C6EF1BE0025E351 /* SquirrelConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BDB21221C6EF1BE0025E351 /* SquirrelConfig.m */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; A45578F51146A75200592C6E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A45578F41146A75200592C6E /* MainMenu.xib */; }; A47C48DF105E8CE8006D528B /* macos_keycode.m in Sources */ = {isa = PBXBuildFile; fileRef = A47C48DE105E8CE8006D528B /* macos_keycode.m */; }; A4B8E1B30F645B870094E08B /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A4B8E1B20F645B870094E08B /* Carbon.framework */; }; A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A4FC48C90F6530EF0069BE81 /* Localizable.strings */; }; - D26434552706A15100857391 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D26434542706A15100857391 /* QuartzCore.framework */; }; E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E93074B60A5C264700470842 /* InputMethodKit.framework */; }; - F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */; }; - F4E457BA2AD97412003F6D79 /* t2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457A92AD97411003F6D79 /* t2emoji.json */; }; - F4E457BB2AD97412003F6D79 /* hk2t.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AA2AD97411003F6D79 /* hk2t.json */; }; - F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AB2AD97411003F6D79 /* EmojiWordEN.ocd2 */; }; - F4E457BD2AD97412003F6D79 /* s2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AC2AD97412003F6D79 /* s2emoji.json */; }; - F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AD2AD97412003F6D79 /* EmojiCategoryHK.ocd2 */; }; - F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AE2AD97412003F6D79 /* EmojiWordT.ocd2 */; }; - F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457AF2AD97412003F6D79 /* EmojiCategoryEN.ocd2 */; }; - F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B02AD97412003F6D79 /* EmojiWordTW.ocd2 */; }; - F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B12AD97412003F6D79 /* EmojiWordHK.ocd2 */; }; - F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B22AD97412003F6D79 /* EmojiWordS.ocd2 */; }; - F4E457C42AD97412003F6D79 /* en2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B32AD97412003F6D79 /* en2emoji.json */; }; - F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B42AD97412003F6D79 /* EmojiCategoryS.ocd2 */; }; - F4E457C62AD97412003F6D79 /* tw2t.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B52AD97412003F6D79 /* tw2t.json */; }; - F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */; }; - F4E457C82AD97412003F6D79 /* hk2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B72AD97412003F6D79 /* hk2emoji.json */; }; - F4E457C92AD97412003F6D79 /* tw2emoji.json in Resources */ = {isa = PBXBuildFile; fileRef = F4E457B82AD97412003F6D79 /* tw2emoji.json */; }; + F478A3592B1078F500D7794A /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F478A3582B1078D600D7794A /* Cocoa.framework */; }; + F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F478A3572B1078D200D7794A /* QuartzCore.framework */; }; + F478A35B2B10795800D7794A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97324FDCFA39411CA2CEA /* AppKit.framework */; }; + F478A35C2B10795B00D7794A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; }; + F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */; }; + F4E457BA2AD97412003F6D79 /* t2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A92AD97411003F6D79 /* t2emoji.json */; }; + F4E457BB2AD97412003F6D79 /* hk2t.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AA2AD97411003F6D79 /* hk2t.json */; }; + F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AB2AD97411003F6D79 /* EmojiWordEN.ocd2 */; }; + F4E457BD2AD97412003F6D79 /* s2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AC2AD97412003F6D79 /* s2emoji.json */; }; + F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AD2AD97412003F6D79 /* EmojiCategoryHK.ocd2 */; }; + F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AE2AD97412003F6D79 /* EmojiWordT.ocd2 */; }; + F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AF2AD97412003F6D79 /* EmojiCategoryEN.ocd2 */; }; + F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B02AD97412003F6D79 /* EmojiWordTW.ocd2 */; }; + F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B12AD97412003F6D79 /* EmojiWordHK.ocd2 */; }; + F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B22AD97412003F6D79 /* EmojiWordS.ocd2 */; }; + F4E457C42AD97412003F6D79 /* en2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B32AD97412003F6D79 /* en2emoji.json */; }; + F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B42AD97412003F6D79 /* EmojiCategoryS.ocd2 */; }; + F4E457C62AD97412003F6D79 /* tw2t.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B52AD97412003F6D79 /* tw2t.json */; }; + F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */; }; + F4E457C82AD97412003F6D79 /* hk2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B72AD97412003F6D79 /* hk2emoji.json */; }; + F4E457C92AD97412003F6D79 /* tw2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B82AD97412003F6D79 /* tw2emoji.json */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -105,7 +107,23 @@ dstPath = opencc; dstSubfolderSpec = 12; files = ( + F4E457C42AD97412003F6D79 /* en2emoji.json in Copy opencc Files */, + F4E457C82AD97412003F6D79 /* hk2emoji.json in Copy opencc Files */, + F4E457BD2AD97412003F6D79 /* s2emoji.json in Copy opencc Files */, + F4E457BA2AD97412003F6D79 /* t2emoji.json in Copy opencc Files */, + F4E457C92AD97412003F6D79 /* tw2emoji.json in Copy opencc Files */, + F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Copy opencc Files */, + F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Copy opencc Files */, + F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Copy opencc Files */, + F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Copy opencc Files */, + F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Copy opencc Files */, + F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Copy opencc Files */, + F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Copy opencc Files */, + F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Copy opencc Files */, + F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Copy opencc Files */, + F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Copy opencc Files */, 77AA68142588916F00A592E2 /* hk2s.json in Copy opencc Files */, + F4E457BB2AD97412003F6D79 /* hk2t.json in Copy opencc Files */, 77AA68152588916F00A592E2 /* HKVariants.ocd2 in Copy opencc Files */, 77AA68162588916F00A592E2 /* HKVariantsRev.ocd2 in Copy opencc Files */, 77AA68172588916F00A592E2 /* HKVariantsRevPhrases.ocd2 in Copy opencc Files */, @@ -126,6 +144,7 @@ 77AA68272588916F00A592E2 /* TSCharacters.ocd2 in Copy opencc Files */, 77AA68282588916F00A592E2 /* TSPhrases.ocd2 in Copy opencc Files */, 77AA68292588916F00A592E2 /* tw2s.json in Copy opencc Files */, + F4E457C62AD97412003F6D79 /* tw2t.json in Copy opencc Files */, 77AA682D2588916F00A592E2 /* TWVariants.ocd2 in Copy opencc Files */, 77AA682E2588916F00A592E2 /* TWVariantsRev.ocd2 in Copy opencc Files */, 77AA682F2588916F00A592E2 /* TWVariantsRevPhrases.ocd2 in Copy opencc Files */, @@ -195,7 +214,6 @@ /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; @@ -279,8 +297,9 @@ A47C48DE105E8CE8006D528B /* macos_keycode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = macos_keycode.m; sourceTree = ""; }; A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; A4FC48CA0F6530EF0069BE81 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - D26434542706A15100857391 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; + F478A3572B1078D200D7794A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; + F478A3582B1078D600D7794A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; @@ -308,10 +327,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D26434552706A15100857391 /* QuartzCore.framework in Frameworks */, - 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */, + F478A3592B1078F500D7794A /* Cocoa.framework in Frameworks */, E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */, + F478A35B2B10795800D7794A /* AppKit.framework in Frameworks */, A4B8E1B30F645B870094E08B /* Carbon.framework in Frameworks */, + F478A35C2B10795B00D7794A /* Foundation.framework in Frameworks */, 447765C925C30E97002415AF /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -344,7 +365,6 @@ children = ( 44CD640915E2633D0021234E /* librime.1.dylib */, 447765C725C30E6B002415AF /* Sparkle.framework */, - 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; @@ -352,10 +372,12 @@ 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( - A4B8E1B20F645B870094E08B /* Carbon.framework */, - E93074B60A5C264700470842 /* InputMethodKit.framework */, 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + A4B8E1B20F645B870094E08B /* Carbon.framework */, + F478A3582B1078D600D7794A /* Cocoa.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + E93074B60A5C264700470842 /* InputMethodKit.framework */, + F478A3572B1078D200D7794A /* QuartzCore.framework */, ); name = "Other Frameworks"; sourceTree = ""; @@ -386,14 +408,14 @@ 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( + 446C01D61F767BD400A6C23E /* Assets.xcassets */, + 44F7708E152B3334005CF491 /* dsa_pub.pem */, 44986A93184B421700B3278D /* LICENSE.txt */, 44986A94184B421700B3278D /* README.md */, - 44F7708E152B3334005CF491 /* dsa_pub.pem */, - A4FC48C90F6530EF0069BE81 /* Localizable.strings */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + A4FC48C90F6530EF0069BE81 /* Localizable.strings */, A45578F41146A75200592C6E /* MainMenu.xib */, - 446C01D61F767BD400A6C23E /* Assets.xcassets */, ); name = Resources; sourceTree = ""; @@ -401,7 +423,6 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( - D26434542706A15100857391 /* QuartzCore.framework */, 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, ); @@ -541,7 +562,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1500; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Squirrel" */; compatibilityVersion = "Xcode 15.0"; @@ -570,30 +591,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - F4E457BB2AD97412003F6D79 /* hk2t.json in Resources */, - F4E457C42AD97412003F6D79 /* en2emoji.json in Resources */, - F4E457C12AD97412003F6D79 /* EmojiWordTW.ocd2 in Resources */, - 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, - F4E457BA2AD97412003F6D79 /* t2emoji.json in Resources */, - A45578F51146A75200592C6E /* MainMenu.xib in Resources */, - F4E457C52AD97412003F6D79 /* EmojiCategoryS.ocd2 in Resources */, - F4E457C22AD97412003F6D79 /* EmojiWordHK.ocd2 in Resources */, 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */, - F4E457BC2AD97412003F6D79 /* EmojiWordEN.ocd2 in Resources */, + 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, A4FC48CB0F6530EF0069BE81 /* Localizable.strings in Resources */, - F4E457C62AD97412003F6D79 /* tw2t.json in Resources */, + A45578F51146A75200592C6E /* MainMenu.xib in Resources */, 44986A95184B421700B3278D /* LICENSE.txt in Resources */, - F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Resources */, - F4E457C92AD97412003F6D79 /* tw2emoji.json in Resources */, 44986A96184B421700B3278D /* README.md in Resources */, - F4E457BF2AD97412003F6D79 /* EmojiWordT.ocd2 in Resources */, - F4E457C32AD97412003F6D79 /* EmojiWordS.ocd2 in Resources */, - F4E457C82AD97412003F6D79 /* hk2emoji.json in Resources */, - F4E457C02AD97412003F6D79 /* EmojiCategoryEN.ocd2 in Resources */, - F4E457BE2AD97412003F6D79 /* EmojiCategoryHK.ocd2 in Resources */, - F4E457BD2AD97412003F6D79 /* s2emoji.json in Resources */, - 44F7708F152B3334005CF491 /* dsa_pub.pem in Resources */, - F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -656,6 +660,8 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; @@ -691,7 +697,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -711,6 +717,8 @@ C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; @@ -744,7 +752,7 @@ "$(inherited)", "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)", ); - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.15; OTHER_CODE_SIGN_FLAGS = "--deep"; OTHER_CPLUSPLUSFLAGS = ( "-DLEOPARD", @@ -766,6 +774,8 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = RimeIcon; + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -813,7 +823,7 @@ /usr/local/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.15; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; @@ -825,6 +835,8 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_APPICON_NAME = RimeIcon; + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; @@ -869,7 +881,7 @@ /usr/lib, ); LIBRARY_SEARCH_PATHS = "$(SRCROOT)/lib"; - MACOSX_DEPLOYMENT_TARGET = "$(RECOMMENDED_MACOSX_DEPLOYMENT_TARGET)"; + MACOSX_DEPLOYMENT_TARGET = 10.15; ONLY_ACTIVE_ARCH = NO; SDKROOT = macosx; SYSTEM_HEADER_SEARCH_PATHS = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/Tk.framework/Headers; diff --git a/librime b/librime index e69533de7..0272f7419 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit e69533de741d838340dc3f253a6776229f48e70b +Subproject commit 0272f74198b4918db1232c9d91fb4061140cb98d From 42e33914b0e085bb02795e7358dd3b17a4339b7c Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 14 Nov 2023 04:05:38 +0100 Subject: [PATCH 148/164] compatible with predictor && cleanup codes --- INSTALL.md | 2 +- Info.plist | 33 +-- Makefile | 1 + SquirrelApplicationDelegate.m | 14 +- SquirrelConfig.m | 4 +- SquirrelInputController.h | 23 +- SquirrelInputController.m | 224 +++++++-------- SquirrelPanel.h | 12 +- SquirrelPanel.m | 498 +++++++++++++++++----------------- action-install.sh | 4 +- autobuild.sh | 3 +- input_source.m | 64 ++--- macos_keycode.h | 1 - macos_keycode.m | 9 +- main.m | 7 +- 15 files changed, 432 insertions(+), 467 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 749d1f605..fecad9710 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -36,7 +36,7 @@ cd squirrel Optionally, checkout Rime plugins (a list of GitHub repo slugs): ``` sh -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... +bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua rime/librime-predict # rime/librime-charcode rime/librime-legacy lotem/librime-proto ... ``` ### Shortcut: get the latest librime release diff --git a/Info.plist b/Info.plist index f09819368..6cdea36f3 100644 --- a/Info.plist +++ b/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - Squirrel + ${EXECUTABLE_NAME} CFBundleIconFile RimeIcon.icns CFBundleIdentifier @@ -13,13 +13,15 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Squirrel + ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) + CFBundleShortVersionString + ${MARKETING_VERSION} ComponentInputModeDict tsInputModeListKey @@ -27,15 +29,14 @@ im.rime.inputmethod.Squirrel.Hant TISInputSourceID - im.rime.inputmethod.Squirrel.Hant + ${PRODUCT_BUNDLE_IDENTIFIER}.Hant TISIntendedLanguage zh-Hant TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hant - Hans + Hani tsInputModeDefaultStateKey @@ -48,22 +49,21 @@ tsInputModeAlternateMenuIconFileKey rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript im.rime.inputmethod.Squirrel.Hans TISInputSourceID - im.rime.inputmethod.Squirrel.Hans + ${PRODUCT_BUNDLE_IDENTIFIER}.Hans TISIntendedLanguage zh-Hans TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hans - Hant + Hani tsInputModeDefaultStateKey @@ -76,22 +76,21 @@ tsInputModeAlternateMenuIconFileKey rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript im.rime.inputmethod.Squirrel.Cant TISInputSourceID - im.rime.inputmethod.Squirrel.Cant + ${PRODUCT_BUNDLE_IDENTIFIER}.Cant TISIntendedLanguage yue-Hant TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hant - Hans + Hani tsInputModeDefaultStateKey @@ -104,7 +103,7 @@ tsInputModeAlternateMenuIconFileKey rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript @@ -142,9 +141,11 @@ TISIconIsTemplate + tsInputMethodCharacterRepertoireKey + + Hani + tsInputMethodIconFileKey rime - tsInputMethodAlternateMenuIconFileKey - rime diff --git a/Makefile b/Makefile index cbe9d4d01..c926eff71 100644 --- a/Makefile +++ b/Makefile @@ -170,4 +170,5 @@ clean: clean-deps: $(MAKE) -C plum clean $(MAKE) -C librime clean + $(MAKE) -C librime deps/clean-src $(MAKE) clean-sparkle diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 89548cef8..cbb860745 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -4,7 +4,7 @@ #import "SquirrelConfig.h" #import "SquirrelPanel.h" -static NSString *const kRimeWikiURL = @"https://github.com/rime/home/wiki"; +static NSString *kRimeWikiURL = @"https://github.com/rime/home/wiki"; @implementation SquirrelApplicationDelegate @@ -148,8 +148,8 @@ - (void)loadSettings { _enableNotifications = ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; - [self.panel loadConfig:_config forDarkMode:NO]; - [self.panel loadConfig:_config forDarkMode:YES]; + [self.panel loadConfig:_config forAppearance:defaultAppear]; + [self.panel loadConfig:_config forAppearance:darkAppear]; } - (void)loadSchemaSpecificSettings:(NSString *)schemaId { @@ -159,11 +159,11 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { SquirrelConfig *schema = [[SquirrelConfig alloc] init]; if ([schema openWithSchemaId:schemaId baseConfig:self.config] && [schema hasSection:@"style"]) { - [self.panel loadConfig:schema forDarkMode:NO]; - [self.panel loadConfig:schema forDarkMode:YES]; + [self.panel loadConfig:schema forAppearance:defaultAppear]; + [self.panel loadConfig:schema forAppearance:darkAppear]; } else { - [self.panel loadConfig:self.config forDarkMode:NO]; - [self.panel loadConfig:self.config forDarkMode:YES]; + [self.panel loadConfig:self.config forAppearance:defaultAppear]; + [self.panel loadConfig:self.config forAppearance:darkAppear]; } [schema close]; } diff --git a/SquirrelConfig.m b/SquirrelConfig.m index b69c0915a..13a850840 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -278,8 +278,8 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { } rime_get_api()->config_end(&optionIter); if (hasStyleSection) { - for (NSUInteger i = 0; i < optionGroup.count; ++i) { - switcher[optionGroup[i]] = optionGroup[reset]; + for (size_t i = 0; i < optionGroup.count; ++i) { + switcher[optionGroup[i]] = optionGroup[(size_t)reset]; optionGroups[optionGroup[i]] = optionGroup; } } diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 92d8c0197..875d5c298 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -3,10 +3,23 @@ @interface SquirrelInputController : IMKInputController -- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index; +typedef enum { + kSELECT = 1, // accepts indices in both digits and selection keys + kDELETE = 2, // only accepts indices in digits, e.g. (int) 1 + kCHOOSE = 3 // only accepts indices in selection keys, e.g. (char) '1' / 'A' +} rimeAction; -@end +typedef enum { + // 0 ... 9 are ordinal digits, used as (int) index + // 0x21 ... 0x7e are ASCII chars (as selection keys) + // other rime keycodes (as function keys), for paging etc. + kEscape = 0xff1b, // XK_Escape + kPageUp = 0xff55, // XK_Page_Up + kPageDown = 0xff56, // XK_Page_Down + kVoidSymbol = 0xffffff // XK_VoidSymbol +} rimeIndex; + +- (void)perform:(rimeAction)action + onIndex:(rimeIndex)index; -#define kSELECT 0x1 -#define kDELETE 0x2 -#define kCHOOSE 0x3 +@end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index ad84b1ed1..216546671 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -10,27 +10,25 @@ @interface SquirrelInputController (Private) - (void)createSession; - (void)destroySession; -- (BOOL)rimeConsumeCommittedText; +- (void)rimeConsumeCommittedText; - (void)updateStyleOptions; - (void)rimeUpdate; -- (void)updateAppOptions; @end const int N_KEY_ROLL_OVER = 50; @implementation SquirrelInputController { NSMutableAttributedString *_preeditString; - NSAttributedString *_originalString; - NSString *_composedString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; - NSUInteger _lastModifier; + NSEventModifierFlags _lastModifier; uint _lastEventCount; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; BOOL _inlineCandidate; + BOOL _showingSwitcherMenu; // app-specific bug fix BOOL _inlinePlaceHolder; BOOL _panellessCommitFix; @@ -40,7 +38,6 @@ @implementation SquirrelInputController { uint _chordKeyCount; NSTimer *_chordTimer; NSTimeInterval _chordDuration; - NSString *_currentApp; } /*! @@ -48,14 +45,15 @@ @implementation SquirrelInputController { @abstract Receive incoming event @discussion This method receives key events from the client application. */ -- (BOOL)handleEvent:(NSEvent *)event client:(id)sender +- (BOOL)handleEvent:(NSEvent *)event + client:(id)sender { // Return YES to indicate the the key input was received and dealt with. // Key processing will not continue in that case. In other words the // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - NSUInteger modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; + NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @autoreleasepool { @@ -83,7 +81,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - ushort keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); + ushort keyCode = (ushort)CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); uint eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + @@ -102,54 +100,46 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender rime_modifiers ^= kLockMask; } [self processKey:rime_keycode modifiers:rime_modifiers]; - } break; + break; case kVK_Shift: - case kVK_RightShift: { - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightShift: + release_mask = modifiers & NSEventModifierFlagShift ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Control: - case kVK_RightControl: { - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightControl: + release_mask = modifiers & NSEventModifierFlagControl ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Option: - case kVK_RightOption: { - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightOption: + release_mask = modifiers & NSEventModifierFlagOption ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; - case kVK_Function: { - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + break; + case kVK_Function: + release_mask = modifiers & NSEventModifierFlagFunction ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Command: - case kVK_RightCommand: { - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightCommand: + release_mask = modifiers & NSEventModifierFlagCommand ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - goto saveStatus; - } - default: break; } [self rimeUpdate]; - saveStatus: _lastEventCount = eventCount; + _lastEventCount = eventCount; } break; case NSEventTypeKeyDown: { - // ignore Command+X hotkeys. - if (modifiers & OSX_COMMAND_MASK) { - break; - } - ushort keyCode = event.keyCode; - NSString *keyChars = ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && - !(modifiers & OSX_ALT_MASK)) ? event.characters : event.charactersIgnoringModifiers; + NSString *keyChars = ((modifiers & NSEventModifierFlagShift) && !(modifiers & NSEventModifierFlagControl) && + !(modifiers & NSEventModifierFlagOption)) ? event.characters : event.charactersIgnoringModifiers; //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], - modifiers & OSX_SHIFT_MASK, - modifiers & OSX_CAPITAL_MASK); + modifiers & NSEventModifierFlagShift, + modifiers & NSEventModifierFlagCapsLock); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) @@ -168,7 +158,8 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender return handled; } -- (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers +- (BOOL)processKey:(int)rime_keycode + modifiers:(int)rime_modifiers { // with linear candidate list, arrow keys may behave differently. Bool is_linear = NSApp.squirrelAppDelegate.panel.linear; @@ -227,10 +218,9 @@ - (void)perform:(rimeAction)action // kDELETE takes ordinal digits (instead of characters) as indexes handled= rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index); } - if (handled && action != kCHOOSE) { + if (handled) { [self rimeUpdate]; } - return handled; } - (void)onChordTimer:(NSTimer *)timer @@ -252,7 +242,8 @@ - (void)onChordTimer:(NSTimer *)timer } } -- (void)updateChord:(int)keycode modifiers:(int)modifiers +- (void)updateChord:(int)keycode + modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); for (uint i = 0; i < _chordKeyCount; ++i) { @@ -314,16 +305,21 @@ - (void)activateServer:(id)sender if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } - _preeditString = nil; - _originalString = nil; - _composedString = nil; + + if (!_session || !RimeFindSession(_session)) { + _session = RimeCreateSession(); + } + [super activateServer:sender]; } -- (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient +- (instancetype)initWithServer:(IMKServer *)server + delegate:(id)delegate + client:(id)inputClient { //NSLog(@"initWithServer:delegate:client:"); if (self = [super initWithServer:server delegate:delegate client:inputClient]) { [self createSession]; + _preeditString = [[NSMutableAttributedString alloc] init]; } return self; } @@ -332,6 +328,7 @@ - (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); [self commitComposition:sender]; + [super deactivateServer:sender]; } /*! @@ -362,6 +359,7 @@ - (void)commitComposition:(id)sender - (void)deploy:(id)sender { [NSApp.squirrelAppDelegate deploy:sender]; + [self createSession]; } - (void)syncUserData:(id)sender @@ -376,7 +374,8 @@ - (void)configure:(id)sender - (void)checkForUpdates:(id)sender { - [NSApp.squirrelAppDelegate.updater performSelector:@selector(checkForUpdates:) withObject:sender]; + [NSApp.squirrelAppDelegate.updater performSelector:@selector(checkForUpdates:) + withObject:sender]; } - (void)openWiki:(id)sender @@ -391,12 +390,18 @@ - (NSMenu *)menu - (NSAttributedString *)originalString:(id)sender { - return _originalString; + const char *raw_input = RimeGetInput(_session); + return [[NSAttributedString alloc] initWithString:raw_input ? @(raw_input) : @""]; } - (id)composedString:(id)sender { - return _composedString; + RimeCommitComposition(_session); + RIME_STRUCT(RimeCommit, commit); + RimeGetCommit(_session, &commit); + const char *composedString = commit.text; + RimeFreeCommit(&commit); + return composedString ? @(composedString) : @""; } - (NSArray *)candidates:(id)sender @@ -412,6 +417,7 @@ - (void)hidePalettes - (void)dealloc { [self destroySession]; + _preeditString = nil; } - (NSRange)selectionRange @@ -429,11 +435,8 @@ - (void)commitString:(id)string //NSLog(@"commitString:"); [self.client insertText:string replacementRange:self.replacementRange]; - [self hidePalettes]; - _composedString = nil; - _originalString = nil; - _preeditString = nil; + [_preeditString deleteCharactersInRange:NSMakeRange(0, _preeditString.length)]; } - (void)cancelComposition @@ -453,6 +456,9 @@ - (void)showPreeditString:(NSString *)preedit selRange:(NSRange)range caretPos:(NSUInteger)pos { + if (_inlinePlaceHolder && _candidates.count > 0 && preedit.length == 0) { + preedit = @" "; + } //NSLog(@"showPreeditString: '%@'", preedit); if ([preedit isEqualToString:_preeditString.string] && NSEqualRanges(range, _selRange) && pos == _caretPos) { @@ -494,18 +500,31 @@ - (void)showPanelWithPreedit:(NSString *)preedit [self.client attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view - if (_lastModifier & OSX_CAPITAL_MASK) { - if (NSHeight(inputPos) > NSWidth(inputPos)) { - inputPos.size.height += 26; - inputPos.origin.y -= 26; - } else { - inputPos.size.width += 33; - inputPos.origin.x -= 33; + if (_lastModifier & NSEventModifierFlagCapsLock) { + NSRect screenRect = [[NSScreen mainScreen] frame]; + if (NSIntersectsRect(inputPos, screenRect)) { + screenRect = [[NSScreen mainScreen] visibleFrame]; + if (NSWidth(inputPos) > NSHeight(inputPos)) { + NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos) - 30, NSMinY(inputPos), 27, NSHeight(inputPos)); + if (NSMinX(capslockAccessory) < NSMinX(screenRect)) + capslockAccessory.origin.x = NSMinX(screenRect); + if (NSMaxX(capslockAccessory) > NSMaxX(screenRect)) + capslockAccessory.origin.x = NSMaxX(screenRect) - NSWidth(capslockAccessory); + inputPos = NSUnionRect(inputPos, capslockAccessory); + } else { + NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos), NSMinY(inputPos) - 26, NSWidth(inputPos), 23); + if (NSMinY(capslockAccessory) < NSMinY(screenRect)) + capslockAccessory.origin.y = NSMaxY(screenRect) + 3; + if (NSMaxY(capslockAccessory) > NSMaxY(screenRect)) + capslockAccessory.origin.y = NSMaxY(screenRect) - NSHeight(capslockAccessory); + inputPos = NSUnionRect(inputPos, capslockAccessory); + } } } } - panel.position = inputPos; panel.inputController = self; + panel.level = self.client.windowLevel + 1; + panel.position = inputPos; [panel showPreedit:preedit selRange:selRange caretPos:caretPos @@ -513,9 +532,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit comments:comments highlighted:index pageNum:pageNum - lastPage:lastPage - turnPage:NSNotFound - update:YES]; + lastPage:lastPage]; } @end // SquirrelController @@ -530,7 +547,6 @@ - (void)createSession //NSLog(@"createSession: %@", app); _session = rime_get_api()->create_session(); _schemaId = nil; - if (_session) { char *rime_client = NULL; if (!rime_get_api()->get_property(_session, "client", rime_client, 100) || @@ -547,8 +563,6 @@ - (void)createSession _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; } } - _panellessCommitFix = [appOptions[@"panelless_commit_fix"] boolValue]; - _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; } } @@ -562,7 +576,7 @@ - (void)destroySession [self clearChord]; } -- (BOOL)rimeConsumeCommittedText +- (void)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { @@ -572,9 +586,7 @@ - (BOOL)rimeConsumeCommittedText } [self commitString:commitText]; rime_get_api()->free_commit(&commit); - return YES; } - return NO; } - (void)updateStyleOptions @@ -614,16 +626,12 @@ - (void)updateStyleOptions - (void)rimeUpdate { //NSLog(@"rimeUpdate"); - if ([self rimeConsumeCommittedText]) { - return; - } - - BOOL switcherMenu = RimeGetOption(_session, "dumb"); + [self rimeConsumeCommittedText]; RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style - if (!switcherMenu && (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { + if ((!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { _schemaId = @(status.schema_id); _showingSwitcherMenu = rime_get_api()->get_option(_session, "dumb"); if (!_showingSwitcherMenu) { @@ -655,66 +663,22 @@ - (void)rimeUpdate const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; - // update composed string - if (!preedit || switcherMenu) { - _composedString = @""; - } else if (RimeGetOption(_session, "soft_cursor")) { - int cursorPos = ctx.composition.cursor_pos; - char composed[strlen(preedit) - 3]; - for (size_t i = 0; i < strlen(preedit) - 3; ++i) { - composed[i] = preedit[i < cursorPos ? i : i + 3]; - } - _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; - } else { - _composedString = [@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]; - } + NSUInteger start = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; + NSUInteger end = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; + NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; + NSUInteger length = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.length encoding:NSUTF8StringEncoding].length; + NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; - // update raw input - const char *raw_input = RimeGetInput(_session); - _originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; - - NSUInteger start = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; - NSUInteger end = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; - NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; - if (_inlineCandidate && !switcherMenu) { - const char *candidatePreview = ctx.commit_text_preview; - NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; - if (_inlinePreedit) { - if ((caretPos >= end) && (caretPos < preeditText.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length - caretPos)]]; - } - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start, candidatePreviewText.length - (preeditText.length - end) - start) - caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (preeditText.length - caretPos)]; - } else { - if ((end < caretPos) && (caretPos > start)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; - } else if ((end < preeditText.length) && (caretPos < end)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (preeditText.length - end))]; - } - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) - caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; - } - } else { - if (_inlinePreedit && !switcherMenu) { - [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; - } else { - // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. - [self showPreeditString:(preedit && _inlinePlaceHolder ? @" " : @"") - selRange:NSMakeRange(0, 0) caretPos:0]; - } - } // update candidates - NSMutableArray *candidates = [NSMutableArray array]; - NSMutableArray *comments = [NSMutableArray array]; - for (int i = 0; i < ctx.menu.num_candidates; ++i) { + NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + for (NSUInteger i = 0; i < numCandidate; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; } - [self showPanelWithPreedit:(_inlinePreedit && !switcherMenu ? nil : preeditText) + [self showPanelWithPreedit:(_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText) selRange:NSMakeRange(start, end - start) - caretPos:(switcherMenu ? NSNotFound : caretPos) + caretPos:(_showingSwitcherMenu ? NSNotFound : caretPos) candidates:candidates comments:comments highlighted:(NSUInteger)ctx.menu.highlighted_candidate_index @@ -755,7 +719,7 @@ - (void)rimeUpdate } rime_get_api()->free_context(&ctx); } else { - [NSApp.squirrelAppDelegate.panel hide]; + [self hidePalettes]; } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 180b3e7be..928d4ab52 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -4,6 +4,12 @@ @class SquirrelConfig; @class SquirrelOptionSwitcher; +typedef enum { + defaultAppear = 0, + lightAppear = 0, + darkAppear = 1 +} SquirrelAppear; + @interface SquirrelPanel : NSPanel // Linear candidate list, as opposed to stacked candidate list. @@ -30,9 +36,7 @@ comments:(NSArray *)comments highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage - turnPage:(NSUInteger)turnPage - update:(BOOL)update; + lastPage:(BOOL)lastPage; - (void)hide; @@ -40,7 +44,7 @@ statusShort:(NSString *)messageShort; - (void)loadConfig:(SquirrelConfig *)config - forDarkMode:(BOOL)isDark; + forAppearance:(SquirrelAppear)appear; - (void)loadLabelConfig:(SquirrelConfig *)config; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d8b4d4342..e17b16e2e 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -20,12 +20,12 @@ - (CGPathRef)quartzPath { // Need to begin a path here. CGPathRef immutablePath = NULL; // Then draw the path elements. - NSUInteger numElements = self.elementCount; + NSInteger numElements = self.elementCount; if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; BOOL didClosePath = YES; - for (NSUInteger i = 0; i < numElements; i++) { + for (NSInteger i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { case NSBezierPathElementMoveTo: CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); @@ -62,7 +62,8 @@ - (CGPathRef)quartzPath { } } -@end +@end // NSBezierPath (BezierPathQuartzUtilities) + @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) @@ -132,7 +133,7 @@ - (CGFloat)annotateRubyInRange:(NSRange)range // base string must use only one font so that all fall within one glyph run and the ruby annotation is aligned with no duplicates NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:baseRange.location effectiveRange:NULL]; baseFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)baseFont, (CFStringRef)self.string, - CFRangeMake(baseRange.location, baseRange.length))); + CFRangeMake((int)baseRange.location, (int)baseRange.length))); [self addAttribute:NSFontAttributeName value:baseFont range:baseRange]; CGFloat rubyScale = 0.5; @@ -180,7 +181,8 @@ - (CGFloat)annotateRubyInRange:(NSRange)range return rubyLineHeight; } -@end +@end // NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) + @interface SquirrelTheme : NSObject @@ -230,6 +232,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; +@property(nonatomic, strong, readonly) NSString *selectKeys; @property(nonatomic, strong, readonly) NSArray *labels; @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; @@ -276,7 +279,8 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; -- (void)setLabels:(NSArray *)labels; +- (void)setSelectKeys:(NSString *)selectKeys + labels:(NSArray *)labels; - (void)setCandidateFormat:(NSString *)candidateFormat; @@ -518,7 +522,9 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle _statusParagraphStyle = statusParagraphStyle; } -- (void)setLabels:(NSArray *)labels { +- (void)setSelectKeys:(NSString *)selectKeys + labels:(NSArray *)labels { + _selectKeys = selectKeys; _labels = labels; } @@ -583,7 +589,8 @@ - (void)setAnnotationHeight:(CGFloat)height { } } -@end +@end // SquirrelTheme + @interface SquirrelLayoutManager : NSLayoutManager @end @@ -615,7 +622,7 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange matrix.ty = - origin.y - NSMinY(lineRect) - position.y; CGContextSetTextMatrix(context, matrix); CTRunDraw(run, context, CFRangeMake(0, 0)); - glyphIndex += CTRunGetGlyphCount(run); + glyphIndex += (NSUInteger)CTRunGetGlyphCount(run); } CGContextRestoreGState(context); CFRelease(line); @@ -659,7 +666,8 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager return NSMakeRect(glyphPosition.x, 0.0, width, glyphPosition.y); } -@end +@end // SquirrelLayoutManager + API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutFragment : NSTextLayoutFragment @@ -669,21 +677,17 @@ @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { - BOOL isVertical = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; NSArray *lineFragments = self.textLineFragments; for (NSTextLineFragment *lineFrag in lineFragments) { - CGRect lineRect = lineFrag.typographicBounds; NSFont *refFont = [lineFrag.attributedString attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - NSParagraphStyle *rulerStyle = [lineFrag.attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; - CGFloat lineHeight = rulerStyle.minimumLineHeight; - CGFloat refFontHeight = refFont.ascender - refFont.descender; CGPoint renderOrigin = CGPointMake(point.x + NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, - point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y - refFontHeight / 2 + (isVertical ? 0.0 : lineHeight / 2 + refFont.descender)); + point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + refFont.descender); [lineFrag drawAtPoint:renderOrigin inContext:context]; } } -@end +@end // SquirrelTextLayoutFragment + API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutManager : NSTextLayoutManager @@ -697,7 +701,8 @@ - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutMan return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement range:textElement.elementRange]; } -@end +@end // SquirrelTextLayoutManager + @interface SquirrelView : NSView @@ -715,7 +720,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) CAShapeLayer *shape; @property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; -@property(nonatomic, readonly) BOOL isDark; +@property(nonatomic, readonly) SquirrelAppear appear; - (void)drawViewWithInsets:(NSEdgeInsets)insets candidateRanges:(NSArray *)candidateRanges @@ -743,23 +748,24 @@ - (BOOL)wantsUpdateLayer { return YES; } -- (BOOL)isDark { - if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { - return YES; +- (SquirrelAppear)appear { + if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames: + @[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { + return darkAppear; } - return NO; + return defaultAppear; } - (BOOL)allowsVibrancy { return YES; } -- (SquirrelTheme *)selectTheme:(BOOL)isDark { - return isDark ? _darkTheme : _defaultTheme; +- (SquirrelTheme *)selectTheme:(SquirrelAppear)appear { + return appear == darkAppear ? _darkTheme : _defaultTheme; } - (SquirrelTheme *)currentTheme { - return [self selectTheme:self.isDark]; + return [self selectTheme:self.appear]; } - (instancetype)initWithFrame:(NSRect)frameRect { @@ -815,8 +821,10 @@ - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(maco return nil; } else { NSTextContentStorage *contentStorage = _textView.textContentStorage; - id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location withOffset:charRange.location]; - id endLocation = [contentStorage locationFromLocation:startLocation withOffset:charRange.length]; + id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location + withOffset:(NSInteger)charRange.location]; + id endLocation = [contentStorage locationFromLocation:startLocation + withOffset:(NSInteger)charRange.length]; return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; } } @@ -1328,7 +1336,7 @@ - (void)updateLayer { if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { borderPath = [backgroundPath copy]; [borderPath appendBezierPath:textContainerPath]; - borderPath.windingRule = NSEvenOddWindingRule; + borderPath.windingRule = NSWindingRuleEvenOdd; } // set layers @@ -1367,7 +1375,7 @@ - (void)updateLayer { backgroundLayer.fillColor = [theme.preeditBackgroundColor CGColor]; if (!candidateBlockPath.empty) { [textContainerPath appendBezierPath:candidateBlockPath]; - textContainerPath.windingRule = NSEvenOddWindingRule; + textContainerPath.windingRule = NSWindingRuleEvenOdd; backgroundLayer.path = [textContainerPath quartzPath]; backgroundLayer.fillRule = kCAFillRuleEvenOdd; CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; @@ -1377,7 +1385,7 @@ - (void)updateLayer { } } if (theme.translucency > 0) { - panelLayer.opacity = 1.0 - theme.translucency; + panelLayer.opacity = (float)(1.0 - theme.translucency); } if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; @@ -1426,13 +1434,15 @@ - (void)updateLayer { if (theme.tabled) { CAShapeLayer *horzGridLayer = [[CAShapeLayer alloc] init]; horzGridLayer.path = [candidateHorzGridPath quartzPath]; - horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; horzGridLayer.lineWidth = theme.edgeInset.height / 2; horzGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:horzGridLayer]; CAShapeLayer *vertGridLayer = [[CAShapeLayer alloc] init]; vertGridLayer.path = [candidateVertGridPath quartzPath]; - vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; vertGridLayer.lineWidth = theme.edgeInset.width / 2; vertGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:vertGridLayer]; @@ -1469,7 +1479,8 @@ - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { return NO; } -@end +@end // SquirrelView + @implementation SquirrelPanel { SquirrelView *_view; @@ -1479,17 +1490,12 @@ @implementation SquirrelPanel { NSSize _maxSize; CGFloat _textWidthLimit; - NSString *_preedit; - NSRange _selRange; - NSUInteger _caretPos; - NSArray *_candidates; - NSArray *_comments; + NSUInteger _numCandidates; NSUInteger _index; - NSUInteger _pageNum; NSUInteger _turnPage; + BOOL _firstPage; BOOL _lastPage; - BOOL _mouseDown; NSPoint _scrollLocus; BOOL _initPosition; @@ -1521,8 +1527,8 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -- (void)initializeUIStyleForDarkMode:(BOOL)isDark { - SquirrelTheme *theme = [_view selectTheme:isDark]; +- (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { + SquirrelTheme *theme = [_view selectTheme:appear]; NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; @@ -1617,7 +1623,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingParagraphStyle:pagingParagraphStyle statusParagraphStyle:statusParagraphStyle]; - [theme setLabels:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"0"]]; + [theme setSelectKeys:@"12345" + labels:@[@"1", @"2", @"3", @"4", @"5"]]; [theme setCandidateFormat:kDefaultCandidateFormat]; } @@ -1629,7 +1636,6 @@ - (instancetype)init { if (self) { self.alphaValue = 1.0; - self.level = kCGCursorWindowLevel - 10; self.hasShadow = NO; self.opaque = NO; self.displaysWhenScreenProfileChanges = YES; @@ -1647,8 +1653,8 @@ - (instancetype)init { [contentView addSubview:_view.textView]; self.contentView = contentView; - [self initializeUIStyleForDarkMode:NO]; - [self initializeUIStyleForDarkMode:YES]; + [self initializeUIStyleForAppearance:defaultAppear]; + [self initializeUIStyleForAppearance:darkAppear]; _maxSize = NSZeroSize; _initPosition = YES; } @@ -1656,72 +1662,63 @@ - (instancetype)init { } - (void)sendEvent:(NSEvent *)event { + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; + NSUInteger cursorIndex = NSNotFound; switch (event.type) { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || - cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) { - _index = cursorIndex; - _mouseDown = YES; + case NSEventTypeLeftMouseUp: + if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index) { + rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; + [_inputController perform:kSELECT onIndex:indexChar]; + } else if (cursorIndex == _turnPage) { + rimeIndex indexChar = cursorIndex == NSPageUpFunctionKey ? kPageUp : + (cursorIndex == NSPageDownFunctionKey ? kPageDown : kVoidSymbol); + [_inputController perform:kSELECT onIndex:indexChar]; } } - } break; - case NSEventTypeLeftMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index || cursorIndex == _turnPage) { - [_inputController perform:kSELECT onIndex:cursorIndex]; - } - } - _mouseDown = NO; - } break; - case NSEventTypeRightMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { - [_inputController perform:kDELETE onIndex:cursorIndex]; + break; + case NSEventTypeRightMouseUp: + if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index) { + [_inputController perform:kDELETE onIndex:(rimeIndex)cursorIndex]; + } else if (cursorIndex == _turnPage) { + [_inputController perform:kSELECT onIndex:kEscape]; } } - _mouseDown = NO; - } break; - case NSEventTypeMouseEntered: { - self.acceptsMouseMovedEvents = YES; - } break; - case NSEventTypeMouseExited: { - self.acceptsMouseMovedEvents = NO; - } break; - case NSEventTypeMouseMoved: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; + break; + case NSEventTypeMouseMoved: if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { - [_inputController perform:kCHOOSE onIndex:cursorIndex]; + if (cursorIndex >= 0 && cursorIndex < _numCandidates && _index != cursorIndex) { _index = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:cursorIndex - pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; + [_inputController perform:kCHOOSE onIndex:indexChar]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:_index - pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + if (_turnPage == NSPageUpFunctionKey) { + [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:_view.currentTheme.pagingAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + cursorIndex = _firstPage ? NSBeginFunctionKey : NSPageUpFunctionKey; + } else if (_turnPage == NSPageDownFunctionKey) { + [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + [_view.textStorage addAttributes:_view.currentTheme.pagingAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; + cursorIndex = _lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + } + [_view drawViewWithInsets:_view.insets + candidateRanges:_view.candidateRanges + highlightedIndex:_view.highlightedIndex + preeditRange:_view.preeditRange + highlightedPreeditRange:_view.highlightedPreeditRange + pagingRange:_view.pagingRange + pagingButton:cursorIndex]; + [self show]; } } - } break; - case NSEventTypeLeftMouseDragged: { - _mouseDown = NO; + break; + case NSEventTypeLeftMouseDragged: _maxSize = NSZeroSize; // reset the remember_size references after moving the panel [self performWindowDragWithEvent:event]; - } break; + break; case NSEventTypeScrollWheel: { SquirrelTheme *theme = _view.currentTheme; CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + @@ -1738,24 +1735,24 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageDown : kPageUp)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { - [_inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; + [_inputController perform:kSELECT onIndex:kPageUp]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageUp : kPageDown)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; + [_inputController perform:kSELECT onIndex:kPageDown]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } } } break; default: + [super sendEvent:event]; break; } - [super sendEvent:event]; } - (void)getCurrentScreen { @@ -1780,7 +1777,7 @@ - (void)getTextWidthLimit { } if (theme.tabled) { CGFloat tabInterval = theme.separatorWidth * 2; - _textWidthLimit = round((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; + _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; } _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX); } @@ -1788,7 +1785,7 @@ - (void)getTextWidthLimit { // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: - (_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; + (_view.appear == darkAppear ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; } @@ -1797,7 +1794,6 @@ - (void)show { SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; NSEdgeInsets insets = _view.insets; - CGFloat offsetHeight = MAX(kOffsetHeight, round(MAX(NSWidth(_position), NSHeight(_position)) / 2)); CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = _screen.visibleFrame; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; @@ -1812,8 +1808,8 @@ - (void)show { } } if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + offsetHeight) - : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) + if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) + : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect)))) && theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { @@ -1823,8 +1819,8 @@ - (void)show { [textContainer setSize:NSMakeSize(_maxSize.width, textHeightLimit)]; } } - if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? offsetHeight : 0)) - : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : offsetHeight))) { + if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) + : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : kOffsetHeight))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { @@ -1833,61 +1829,78 @@ - (void)show { } } - _initPosition |= NSIntersectsRect(self.frame, _position); NSRect windowRect; - if (theme.vertical) { - windowRect = NSMakeRect(NSMaxX(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, - NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, - NSHeight(maxContentRect) + insets.top + insets.bottom, - NSWidth(maxContentRect) + insets.left + insets.right); - _initPosition |= NSIntersectsRect(windowRect, _position); - if (_initPosition) { - // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + offsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -offsetHeight) - NSHeight(windowRect); - } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : offsetHeight); - } - // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - (sweepVertical ? offsetHeight : 0) - NSWidth(windowRect); - if (!sweepVertical && _view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); - } + if (_statusMessage != nil) { // following system UI, middle-align status message with cursor + _initPosition = YES; + if (theme.vertical) { + windowRect.size.width = NSHeight(maxContentRect) + insets.top + insets.bottom; + windowRect.size.height = NSWidth(maxContentRect) + insets.left + insets.right; + } else { + windowRect.size.width = NSWidth(maxContentRect) + insets.left + insets.right; + windowRect.size.height = NSHeight(maxContentRect) + insets.top + insets.bottom; + } + if (sweepVertical) { // vertically centre-align (MidY) in screen coordinates + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - windowRect.size.width; + windowRect.origin.y = NSMidY(_position) - windowRect.size.height / 2; + } else { // horizontally centre-align (MidX) in screen coordinates + windowRect.origin.x = NSMidX(_position) - windowRect.size.width / 2; + windowRect.origin.y = NSMinY(_position) - kOffsetHeight - windowRect.size.height; } } else { - windowRect = NSMakeRect(NSMinX(self.frame), - NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, - NSWidth(maxContentRect) + insets.left + insets.right, - NSHeight(maxContentRect) + insets.top + insets.bottom); - _initPosition |= NSIntersectsRect(windowRect, _position); - if (_initPosition) { - if (sweepVertical) { - // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) { - windowRect.origin.x = NSMinX(_position) - offsetHeight - NSWidth(windowRect); + if (theme.vertical) { // anchor is the top right corner in screen coordinates (MaxX, MaxY) + windowRect = NSMakeRect(NSMaxX(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right); + _initPosition |= NSIntersectsRect(windowRect, _position); + if (_initPosition) { + // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa + if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : insets.left + kOffsetHeight) - NSWidth(maxContentRect) - insets.right; } else { - windowRect.origin.x = NSMaxX(_position) + offsetHeight; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); + } + // Make the right edge of candidate block fixed at the left of cursor + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSHeight(maxContentRect) - insets.top - insets.bottom; + if (!sweepVertical && _view.preeditRange.length > 0) { + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); + } + } + } else { // anchor is the top left corner in screen coordinates (MinX, MaxY) + windowRect = NSMakeRect(NSMinX(self.frame), + NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom); + _initPosition |= NSIntersectsRect(windowRect, _position); + if (_initPosition) { + if (sweepVertical) { + // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa + if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + } else { + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + } + windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); + } else { + windowRect.origin.x = NSMinX(_position) - insets.left; + windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); } - windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); - } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, - NSMinY(_position) - offsetHeight - NSHeight(windowRect)); } } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (_initPosition && sweepVertical ? NSMinX(_position) - offsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_position) - kOffsetHeight, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = _initPosition && sweepVertical ? NSMaxX(_position) + offsetHeight : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_position) + kOffsetHeight, NSMinX(screenRect)) : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = _initPosition && !sweepVertical ? NSMaxY(_position) + offsetHeight : NSMinY(screenRect); + windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_position) + kOffsetHeight, NSMinY(screenRect)) : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (_initPosition && !sweepVertical ? NSMinY(_position) - offsetHeight : NSMaxY(screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_position) - kOffsetHeight, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); } if (theme.vertical) { @@ -1900,11 +1913,11 @@ - (void)show { // rotate the view, the core in vertical mode! if (theme.vertical) { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:YES]; + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; [self.contentView setBoundsRotation:-90.0]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:YES]; + [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; [self.contentView setBoundsRotation:0.0]; [self.contentView setBoundsOrigin:NSZeroPoint]; } @@ -1926,8 +1939,10 @@ - (void)show { [_back setHidden:YES]; } [self setAlphaValue:theme.alpha]; + [self display]; [self orderFront:nil]; - _initPosition = NO; + // reset to initial position after showing status message + _initPosition = _statusMessage != nil; // voila ! } @@ -1941,10 +1956,13 @@ - (void)hide { _initPosition = YES; } -- (void)setLayoutForRange:(NSRange)charRange - withReferenceFont:(NSFont *)refFont - paragraphStyle:(NSParagraphStyle *)style { +- (void)setLayoutForRange:(NSRange)charRange { BOOL verticalLayout = _view.currentTheme.vertical; + NSFont *refFont = [_view.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) + atIndex:charRange.location + effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; + NSParagraphStyle *style = [_view.textStorage attribute:NSParagraphStyleAttributeName + atIndex:charRange.location effectiveRange:NULL]; CGFloat refFontHeight = refFont.ascender - refFont.descender; CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); @@ -1954,16 +1972,20 @@ - (void)setLayoutForRange:(NSRange)charRange enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - NSFont *refFont = attrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)][CFBridgingRelease(kCTBaselineReferenceFont)]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue] + lineHeight / 2 - refFontHeight / 2; - NSNumber *superscript = attrs[NSSuperscriptAttributeName]; - if (superscript) { - NSFont *runFont = verticalLayout ? [attrs[NSFontAttributeName] verticalFont] : attrs[NSFontAttributeName]; - baselineOffset += superscript.integerValue == 1 ? runFont.descender / 3 : runFont.ascender / 3; + NSFont *runFont = attrs[NSFontAttributeName]; + NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; + if (superscript != 0) { + baselineOffset += superscript == 1 ? runFont.ascender + runFont.descender : -runFont.descender; + } + if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + if (!verticalLayout) { + baselineOffset -= (runFont.ascender - runFont.descender) / 16; + } else if (superscript == 1) { + baselineOffset -= runFont.ascender + runFont.descender - (runFont.ascender - runFont.descender) / 16; + } else if (superscript == -1) { + baselineOffset += runFont.descender - (runFont.ascender - runFont.descender) / 16; + } } [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset) range:range]; @@ -1973,11 +1995,6 @@ - (void)setLayoutForRange:(NSRange)charRange NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange range, BOOL *stop) { - NSFont *refFont = [layoutManager.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:range.location effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); // typesetting glyphs NSUInteger j = range.location; @@ -1987,29 +2004,28 @@ - (void)setLayoutForRange:(NSRange)charRange NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; NSDictionary *attrs = [layoutManager.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; NSFont *runFont = attrs[NSFontAttributeName]; - NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; - NSNumber *superscript = attrs[NSSuperscriptAttributeName]; + NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; if (verticalLayout) { NSNumber *verticalGlyph = attrs[NSVerticalGlyphFormAttributeName]; if (verticalGlyph ? verticalGlyph.boolValue : YES) { runFont = runFont.verticalFont; - systemFont = systemFont.verticalFont; } } - CGFloat runFontHeight = runFont.ascender - runFont.descender; - CGFloat systemFontHeight = systemFont.ascender - systemFont.descender; - if (superscript) { - offset += superscript.integerValue == 1 ? refFont.ascender / 3 : refFont.descender / 3; + if (superscript != 0) { + offset += superscript == 1 ? refFont.ascender - runFont.ascender : refFont.descender - runFont.descender; + if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + offset += superscript == 1 ? runFont.pointSize / 16 : runFont.descender + runFont.pointSize * 219 / 800; + } } if (verticalLayout) { if ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassRoman)] || !runFont.vertical) { runGlyphPosition.y = alignment - offset + refFont.xHeight / 2; } else { - runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) / 3 : 0.0); - runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) * 2 / 3 : 0.0; + runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] && superscript == 0 ? runFont.pointSize / 16 : 0.0); + runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? runFont.pointSize * 119 / 800 : 0.0; } } else { runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - refFont.xHeight / 2 : 0.0); @@ -2079,23 +2095,14 @@ - (void)showPreedit:(NSString *)preedit comments:(NSArray *)comments highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage - turnPage:(NSUInteger)turnPage - update:(BOOL)update { - if (update) { - _preedit = preedit; - _selRange = selRange; - _caretPos = caretPos; - _candidates = candidates; - _comments = comments; - _index = index; - _pageNum = pageNum; - _lastPage = lastPage; - } - + lastPage:(BOOL)lastPage { [self getTextWidthLimit]; - NSUInteger numCandidates = candidates.count; - if (numCandidates > 0 || (preedit && preedit.length)) { + _numCandidates = candidates.count; + _index = _numCandidates == 0 ? NSNotFound : index; + _firstPage = pageNum == 0; + _lastPage = lastPage; + _turnPage = NSNotFound; + if (_numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { [_statusTimer invalidate]; @@ -2111,16 +2118,6 @@ - (void)showPreedit:(NSString *)preedit return; } - if (numCandidates == 0) { - _index = index = NSNotFound; - } - _turnPage = turnPage; - if (_turnPage == NSPageUpFunctionKey) { - turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } else if (_turnPage == NSPageDownFunctionKey) { - turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; - } - SquirrelTheme *theme = _view.currentTheme; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -2132,7 +2129,7 @@ - (void)showPreedit:(NSString *)preedit [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); - NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; + NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:_numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); NSMutableParagraphStyle *paragraphStyleCandidate; @@ -2159,13 +2156,13 @@ - (void)showPreedit:(NSString *)preedit } // force caret to be rendered horizontally in vertical layout if (caretPos != NSNotFound) { - [preeditLine addAttributes:@{NSVerticalGlyphFormAttributeName: @NO} - range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; + [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@NO + range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; - if (numCandidates > 0) { + if (_numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { goto typesetter; @@ -2179,7 +2176,7 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate = [theme.paragraphStyle copy]; } CGFloat tabInterval = theme.separatorWidth * 2; - for (NSUInteger idx = 0; idx < numCandidates; ++idx) { + for (NSUInteger idx = 0; idx < _numCandidates; ++idx) { // attributed labels are already included in candidateFormats NSMutableAttributedString *item = (idx == index) ? [theme.candidateHighlightedFormats[idx] mutableCopy] : [theme.candidateFormats[idx] mutableCopy]; NSRange candidateRange = [item.string rangeOfString:@"%@"]; @@ -2226,7 +2223,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *separator = [theme.separator mutableCopy]; if (theme.tabled) { // fill gaps to make cells 2^n tabs wide CGFloat widthInTabs = (ceil([text attributedSubstringFromRange:candidateRanges.lastObject.rangeValue].size.width) + theme.separatorWidth) / tabInterval; - NSUInteger numPaddingTabs = ceil(widthInTabs / 2) * 2 - ceil(widthInTabs); + NSUInteger numPaddingTabs = (NSUInteger)(ceil(widthInTabs / 2) * 2 - ceil(widthInTabs)); [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; } [text appendAttributedString:separator]; @@ -2241,7 +2238,7 @@ - (void)showPreedit:(NSString *)preedit } // for linear layout, middle-truncate candidates that are longer than one line if (theme.linear && ceil(item.size.width + theme.separatorWidth) > _textWidthLimit) { - if (idx < numCandidates - 1 || theme.showPaging) { + if (idx < _numCandidates - 1 || theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; } NSMutableParagraphStyle *paragraphStyleTruncating = [paragraphStyleCandidate mutableCopy]; @@ -2262,11 +2259,6 @@ - (void)showPreedit:(NSString *)preedit initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; [paging appendAttributedString:[[NSAttributedString alloc] initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]]; - if (_turnPage == NSPageUpFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(0, 1)]; - } else if (_turnPage == NSPageDownFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 1, 1)]; - } [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; @@ -2315,22 +2307,14 @@ - (void)showPreedit:(NSString *)preedit theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width + theme.separatorWidth / 2); if (preedit) { - [self setLayoutForRange:preeditRange - withReferenceFont:(theme.vertical ? [theme.preeditAttrs[NSFontAttributeName] verticalFont] : theme.preeditAttrs[NSFontAttributeName]) - paragraphStyle:theme.preeditParagraphStyle]; + [self setLayoutForRange:preeditRange]; insets.top = theme.edgeInset.height; } - if (numCandidates > 0) { + if (_numCandidates > 0) { NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); - NSFont *refFont = getTallestFont(@[theme.attrs[NSFontAttributeName], theme.labelAttrs[NSFontAttributeName], - theme.commentAttrs[NSFontAttributeName]], theme.vertical); - [self setLayoutForRange:candidateBlockRange - withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) - paragraphStyle:theme.paragraphStyle]; + [self setLayoutForRange:candidateBlockRange]; if (!theme.linear && pagingRange.length > 0) { - [self setLayoutForRange:pagingRange - withReferenceFont:theme.pagingAttrs[NSFontAttributeName] - paragraphStyle:theme.pagingParagraphStyle]; + [self setLayoutForRange:pagingRange]; insets.bottom = theme.edgeInset.height; } } else { @@ -2346,7 +2330,7 @@ - (void)showPreedit:(NSString *)preedit preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange - pagingButton:turnPage]; + pagingButton:_turnPage]; [self show]; } @@ -2381,9 +2365,7 @@ - (void)showStatus:(NSString *)message { [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; - [self setLayoutForRange:NSMakeRange(0, text.length) - withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) - paragraphStyle:theme.statusParagraphStyle]; + [self setLayoutForRange:NSMakeRange(0, text.length)]; // disable remember_size and fixed line_length for status messages _initPosition = YES; @@ -2533,50 +2515,51 @@ - (void)setAnnotationHeight:(CGFloat)height { } - (void)loadLabelConfig:(SquirrelConfig *)config { - SquirrelTheme *theme = [_view selectTheme:NO]; + SquirrelTheme *theme = [_view selectTheme:defaultAppear]; [SquirrelPanel updateTheme:theme withLabelConfig:config]; - SquirrelTheme *darkTheme = [_view selectTheme:YES]; + SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; } + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config { - int menuSize = [config getInt:@"menu/page_size"] ? : 5; + NSUInteger menuSize = (NSUInteger)[config getInt:@"menu/page_size"] ? : 5; NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; if (selectKeys) { NSString *keyCaps = [[selectKeys uppercaseString] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; - for (int i = 0; i < menuSize; ++i) { + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; } } else { + selectKeys = [@"1234567890" substringToIndex:menuSize]; NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; if (selectLabels) { - for (int i = 0; i < menuSize; ++i) { + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = selectLabels[i]; } } else { - NSString *numerals = @"1234567890"; - for (int i = 0; i < menuSize; ++i) { + NSString *numerals = [selectKeys stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; } } } - [theme setLabels:labels]; + [theme setSelectKeys:selectKeys labels:labels]; } - (void)loadConfig:(SquirrelConfig *)config - forDarkMode:(BOOL)isDark { - SquirrelTheme *theme = [_view selectTheme:isDark]; + forAppearance:(SquirrelAppear)appear { + SquirrelTheme *theme = [_view selectTheme:appear]; NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; - [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forDarkMode:isDark]; + [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forAppearance:appear]; } + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config styleOptions:(NSSet *)styleOptions - forDarkMode:(BOOL)isDark { + forAppearance:(SquirrelAppear)appear { // INTERFACE BOOL linear = NO; BOOL tabled = NO; @@ -2623,7 +2606,7 @@ + (void)updateTheme:(SquirrelTheme *)theme NSColor *highlightedCommentTextColor; NSString *colorScheme; - if (isDark) { + if (appear == darkAppear) { for (NSString *option in styleOptions) { if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; } @@ -2709,6 +2692,8 @@ + (void)updateTheme:(SquirrelTheme *)theme fontDescriptorByAddingAttributes:monoDigitAttrs]; NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:MAX(labelFontSize.doubleValue, 0)] : [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; + NSString *labelString = [theme.labels componentsJoinedByString:@""]; + labelFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)labelFont, (CFStringRef)labelString, CFRangeMake(0, (int)labelString.length))); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) @@ -2774,8 +2759,9 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *refFont = getTallestFont(@[font, labelFont, commentFont], vertical); - refFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)refFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *zhFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)font, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)commentFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *refFont = getTallestFont(@[zhFont, labelFont, zhCommentFont], vertical); labelAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); labelHighlightedAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; @@ -2784,10 +2770,10 @@ + (void)updateTheme:(SquirrelTheme *)theme highlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; commentAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; commentHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; + preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; pagingAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): linear ? labelFont : pagingFont}; - statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? commentFont.verticalFont : commentFont}; + statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhCommentFont.verticalFont : zhCommentFont}; attrs[NSBaselineOffsetAttributeName] = baseOffset; highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; @@ -2809,7 +2795,7 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); // CHROMATICS refinement - if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - isDark) <= 0.4) { + if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - (appear == darkAppear)) <= 0.4) { backgroundColor = inverseColor(backgroundColor); borderColor = inverseColor(borderColor); preeditBackgroundColor = inverseColor(preeditBackgroundColor); @@ -2830,7 +2816,7 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditBackgroundColor = preeditBackgroundColor ? : isNative ? [NSColor windowBackgroundColor] : nil; candidateTextColor = candidateTextColor ? : [NSColor controlTextColor]; highlightedCandidateTextColor = highlightedCandidateTextColor ? : [NSColor selectedMenuItemTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? [NSColor alternateSelectedControlColor] : nil; + highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? [NSColor selectedContentBackgroundColor] : nil; candidateLabelColor = candidateLabelColor ? : isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); commentTextColor = commentTextColor ? : [NSColor secondaryLabelColor]; @@ -2862,7 +2848,7 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditLinespace:MAX(spacing.doubleValue, 0) alpha:(alpha ? MIN(MAX(alpha.doubleValue, 0.0), 1.0) : 1.0) translucency:(translucency ? MIN(MAX(translucency.doubleValue, 0.0), 1.0) : 0.0) - lineLength:lineLength.doubleValue ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 + lineLength:lineLength.doubleValue > 0 ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 showPaging:showPaging.boolValue rememberSize:rememberSize.boolValue tabled:tabled @@ -2899,4 +2885,4 @@ + (void)updateTheme:(SquirrelTheme *)theme [theme setStatusMessageType:statusMessageType]; } -@end +@end // SquirrelPanel diff --git a/action-install.sh b/action-install.sh index 68bdc5126..0c0aefdf9 100755 --- a/action-install.sh +++ b/action-install.sh @@ -2,8 +2,8 @@ set -e -rime_version=1.9.0-m -rime_git_hash=275b399 +rime_version=1.9.0-e +rime_git_hash=095475e rime_archive="rime-${rime_git_hash}-macOS.tar.bz2" rime_download_url="https://github.com/rime/librime/releases/download/${rime_version}/${rime_archive}" diff --git a/autobuild.sh b/autobuild.sh index a971e9d06..05ee42ab5 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -2,12 +2,13 @@ make clean clean-deps # git submodule update --init --recursive -bash librime/install-plugins.sh rime/librime-sample lotem/librime-octagram hchunhui/librime-lua +bash librime/install-plugins.sh lotem/librime-octagram hchunhui/librime-lua rime/librime-predict export CMAKE_GENERATOR=Ninja export BUILD_UNIVERSAL=1 bash librime/install-boost.sh export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" +make -C librime deps # export PATH="/opt/homebrew/opt/llvm/bin:/usr/local/opt/llvm/bin:$PATH" make -C librime diff --git a/input_source.m b/input_source.m index d13664fc2..2d4a0cab0 100644 --- a/input_source.m +++ b/input_source.m @@ -1,14 +1,14 @@ #import -static const unsigned char kInstallLocation[] = +static const char kInstallLocation[] = "/Library/Input Methods/Squirrel.app"; -static NSString *const kHansInputModeID = - @"im.rime.inputmethod.Squirrel.Hans"; -static NSString *const kHantInputModeID = - @"im.rime.inputmethod.Squirrel.Hant"; -static NSString *const kCantInputModeID = - @"im.rime.inputmethod.Squirrel.Cant"; +static CFStringRef kHansInputModeID = + CFSTR("im.rime.inputmethod.Squirrel.Hans"); +static CFStringRef kHantInputModeID = + CFSTR("im.rime.inputmethod.Squirrel.Hant"); +static CFStringRef kCantInputModeID = + CFSTR("im.rime.inputmethod.Squirrel.Cant"); #define HANS_INPUT_MODE (1 << 0) #define HANT_INPUT_MODE (1 << 1) @@ -16,7 +16,7 @@ void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( - NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); + NULL, (UInt8 *)kInstallLocation, (CFIndex)strlen(kInstallLocation), false); if (installedLocationURL) { TISRegisterInputSource(installedLocationURL); CFRelease(installedLocationURL); @@ -27,17 +27,15 @@ void RegisterInputSource(void) { void ActivateInputSource(int enabled_modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { - TISInputSourceRef inputSource = - (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); - NSString *sourceID = (__bridge NSString *) - TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); + CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if (([sourceID isEqualToString:kHansInputModeID] && - ((enabled_modes & HANS_INPUT_MODE) != 0)) || - ([sourceID isEqualToString:kHantInputModeID] && - ((enabled_modes & HANT_INPUT_MODE) != 0)) || - ([sourceID isEqualToString:kCantInputModeID] && - ((enabled_modes & CANT_INPUT_MODE) != 0))) { + if ((CFStringCompare(sourceID, kHansInputModeID, 0) == 0 && + (enabled_modes & HANS_INPUT_MODE) != 0) || + (CFStringCompare(sourceID, kHantInputModeID, 0) == 0 && + (enabled_modes & HANT_INPUT_MODE) != 0) || + (CFStringCompare(sourceID, kCantInputModeID, 0) == 0 && + (enabled_modes & CANT_INPUT_MODE) != 0)) { OSStatus enableError = TISEnableInputSource(inputSource); if (enableError) { NSLog(@"Error %d. Failed to enable input mode: %@", enableError, sourceID); @@ -62,14 +60,12 @@ void ActivateInputSource(int enabled_modes) { void DeactivateInputSource(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { - TISInputSourceRef inputSource = - (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); - NSString *sourceID = (__bridge NSString *) - TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); + CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kHansInputModeID] || - [sourceID isEqualToString:kHantInputModeID] || - [sourceID isEqualToString:kCantInputModeID]) { + if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0 || + CFStringCompare(sourceID, kHantInputModeID, 0) == 0 || + CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { @@ -89,22 +85,20 @@ int GetEnabledInputModes(void) { int input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { - TISInputSourceRef inputSource = - (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); - NSString *sourceID = (__bridge NSString *) - TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); + CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if ([sourceID isEqualToString:kHansInputModeID] || - [sourceID isEqualToString:kHantInputModeID] || - [sourceID isEqualToString:kCantInputModeID]) { + if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0 || + CFStringCompare(sourceID, kHantInputModeID, 0) == 0 || + CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { - if ([sourceID isEqualToString:kHansInputModeID]) { + if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0) { input_modes |= HANS_INPUT_MODE; - } else if ([sourceID isEqualToString:kHantInputModeID]) { + } else if (CFStringCompare(sourceID, kHantInputModeID, 0) == 0) { input_modes |= HANT_INPUT_MODE; - } else if ([sourceID isEqualToString:kCantInputModeID]) { + } else if (CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { input_modes |= CANT_INPUT_MODE; } } diff --git a/macos_keycode.h b/macos_keycode.h index c68b0e772..51fb37247 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -1,4 +1,3 @@ - #ifndef _MACOS_KEYCODE_H_ #define _MACOS_KEYCODE_H_ diff --git a/macos_keycode.m b/macos_keycode.m index 32eefe081..22a9c7db2 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -1,4 +1,3 @@ - #import "macos_keycode.h" #import @@ -43,7 +42,7 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { //OSX_VK_ENTER_POWERBOOK -> ? { OSX_VK_ESCAPE, XK_Escape }, { OSX_VK_FORWARD_DELETE, XK_Delete }, - //{OSX_VK_HELP, XK_Help}, // the same keycode with OSX_VK_PC_INSERT + //{OSX_VK_HELP, XK_Help}, // the same keycode as OSX_VK_PC_INSERT { OSX_VK_RETURN, XK_Return }, { OSX_VK_SPACE, XK_space }, { OSX_VK_TAB, XK_Tab }, @@ -102,12 +101,16 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { // pc keyboard { OSX_VK_PC_APPLICATION, XK_Menu }, { OSX_VK_PC_INSERT, XK_Insert }, - { OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock }, + //{OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock}, // the same keycode as OSX_VK_KEYPAD_CLEAR { OSX_VK_PC_PAUSE, XK_Pause }, //OSX_VK_PC_POWER -> ? { OSX_VK_PC_PRINTSCREEN, XK_Print }, { OSX_VK_PC_SCROLLLOCK, XK_Scroll_Lock }, + // JIS keyboard + { OSX_VK_JIS_EISUU, XK_Eisu_toggle }, + { OSX_VK_JIS_KANA, XK_Kana_Lock }, + { -1, -1 } }; diff --git a/main.m b/main.m index 275778ecf..9651525cb 100644 --- a/main.m +++ b/main.m @@ -1,4 +1,3 @@ - #import "SquirrelApplicationDelegate.h" #import #import @@ -14,7 +13,7 @@ // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. -const NSString *kConnectionName = @"Squirrel_1_Connection"; +static NSString *kConnectionName = @"Squirrel_1_Connection"; int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--quit", argv[1])) { @@ -65,12 +64,12 @@ int main(int argc, char *argv[]) { // find the bundle identifier and then initialize the input method server NSBundle *main = [NSBundle mainBundle]; IMKServer *server __unused = - [[IMKServer alloc] initWithName:(NSString *)kConnectionName + [[IMKServer alloc] initWithName:kConnectionName bundleIdentifier:main.bundleIdentifier]; // load the bundle explicitly because in this case the input method is a // background only application - [main loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication] topLevelObjects:NULL]; + [main loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication] topLevelObjects:nil]; // opencc will be configured with relative dictionary paths [[NSFileManager defaultManager] From 83e2e993a025da4a971839dd537b4a83ee85338b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 28 Nov 2023 04:30:24 +0100 Subject: [PATCH 149/164] logs --- Assets.xcassets/rime.imageset/rime.svg | 2 +- .../rimeSymbol.imageset/Contents.json | 43 +++++++++++ .../rimeSymbol.fill_Normal.png | Bin 0 -> 5233 bytes .../rimeSymbol.fill_Normal@2x.png | Bin 0 -> 5406 bytes .../rimeSymbol.stroke_Normal.png | Bin 0 -> 5380 bytes .../rimeSymbol.stroke_Normal@2x.png | Bin 0 -> 5446 bytes Base.lproj/MainMenu.xib | 69 ++++++++++++++++-- Squirrel.xcodeproj/project.pbxproj | 17 +++-- SquirrelApplicationDelegate.h | 1 + SquirrelApplicationDelegate.m | 6 ++ SquirrelInputController.m | 9 ++- zh-HK.lproj/MainMenu.strings | 18 +++++ zh-HK.lproj/MainMenu.xib | 59 --------------- zh-Hans.lproj/MainMenu.strings | 18 +++++ zh-Hans.lproj/MainMenu.xib | 59 --------------- zh-Hant.lproj/MainMenu.strings | 18 +++++ zh-Hant.lproj/MainMenu.xib | 59 --------------- 17 files changed, 187 insertions(+), 191 deletions(-) create mode 100644 Assets.xcassets/rimeSymbol.imageset/Contents.json create mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal.png create mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal@2x.png create mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png create mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png create mode 100644 zh-HK.lproj/MainMenu.strings delete mode 100644 zh-HK.lproj/MainMenu.xib create mode 100644 zh-Hans.lproj/MainMenu.strings delete mode 100644 zh-Hans.lproj/MainMenu.xib create mode 100644 zh-Hant.lproj/MainMenu.strings delete mode 100644 zh-Hant.lproj/MainMenu.xib diff --git a/Assets.xcassets/rime.imageset/rime.svg b/Assets.xcassets/rime.imageset/rime.svg index a7126197c..1aa069ca9 100644 --- a/Assets.xcassets/rime.imageset/rime.svg +++ b/Assets.xcassets/rime.imageset/rime.svg @@ -3,7 +3,7 @@ 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 literal 0 HcmV?d00001 diff --git a/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png b/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd156c1bd3c00d0fda4c8e18de2e03244bd0fcf GIT binary patch literal 5380 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 V88c(s$Fl!li!`v%FVl64{Ts~LM(_Xt literal 0 HcmV?d00001 diff --git a/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png b/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4af72fe60cea80b943ca4b8f2ea0860a00b10db0 GIT binary patch literal 5446 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 - + - + + + @@ -22,38 +24,95 @@

      + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 742804b2d..9d6ae439f 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -242,13 +242,11 @@ 44AC95161430CF6000C888FB /* SquirrelApplicationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SquirrelApplicationDelegate.h; sourceTree = ""; }; 44AC95171430CF6000C888FB /* SquirrelApplicationDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SquirrelApplicationDelegate.m; sourceTree = ""; }; 44AC95181430CF6000C888FB /* SquirrelInputController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SquirrelInputController.h; sourceTree = ""; }; - 44AC95191430CF6000C888FB /* SquirrelInputController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SquirrelInputController.m; sourceTree = ""; }; + 44AC95191430CF6000C888FB /* SquirrelInputController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = SquirrelInputController.m; sourceTree = ""; }; 44AEBC7121F569CF00344375 /* punctuation.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = punctuation.yaml; path = data/plum/punctuation.yaml; sourceTree = ""; }; 44AEBC7221F569CF00344375 /* key_bindings.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = key_bindings.yaml; path = data/plum/key_bindings.yaml; sourceTree = ""; }; 44CB5E872585EFAE0022654F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 44CD640915E2633D0021234E /* librime.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librime.1.dylib; path = lib/librime.1.dylib; sourceTree = ""; }; - 44DA191A152B8CB600FB8EF0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.xib"; sourceTree = ""; }; - 44DA191B152B8CBC00FB8EF0 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.xib"; sourceTree = ""; }; 44E21A8E16A653E700C2B08F /* rime_deployer */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_deployer; path = bin/rime_deployer; sourceTree = ""; }; 44E21A8F16A653E700C2B08F /* rime_dict_manager */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = rime_dict_manager; path = bin/rime_dict_manager; sourceTree = ""; }; 44F1EB381431F8270015FD04 /* Squirrel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Squirrel.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -298,11 +296,13 @@ A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; A4FC48CA0F6530EF0069BE81 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; + F402BAF72B1D8F40001A4BD6 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.strings"; sourceTree = ""; }; + F402BAF82B1D8F47001A4BD6 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; + F402BAF92B1D8F57001A4BD6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; F478A3572B1078D200D7794A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; F478A3582B1078D600D7794A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; - F4A90996297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.xib"; sourceTree = ""; }; F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryTW.ocd2; sourceTree = ""; }; F4E457A92AD97411003F6D79 /* t2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = t2emoji.json; sourceTree = ""; }; F4E457AA2AD97411003F6D79 /* hk2t.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hk2t.json; sourceTree = ""; }; @@ -562,6 +562,9 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; + KnownAssetTags = ( + New, + ); LastUpgradeCheck = 1500; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Squirrel" */; @@ -635,10 +638,10 @@ A45578F41146A75200592C6E /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( - 44DA191A152B8CB600FB8EF0 /* zh-Hans */, - 44DA191B152B8CBC00FB8EF0 /* zh-Hant */, - F4A90996297F63AE00D9F520 /* zh-HK */, 44CB5E872585EFAE0022654F /* Base */, + F402BAF72B1D8F40001A4BD6 /* zh-HK */, + F402BAF82B1D8F47001A4BD6 /* zh-Hans */, + F402BAF92B1D8F57001A4BD6 /* zh-Hant */, ); name = MainMenu.xib; sourceTree = ""; diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index a4bbc061e..153a4ba80 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -18,6 +18,7 @@ - (IBAction)syncUserData:(id)sender; - (IBAction)configure:(id)sender; - (IBAction)openWiki:(id)sender; +- (IBAction)openLogFolder:(id)sender; - (void)setupRime; - (void)startRimeWithFullCheck:(BOOL)fullCheck; diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index cbb860745..28b706c92 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -32,6 +32,12 @@ - (IBAction)openWiki:(id)sender [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:kRimeWikiURL]]; } +- (IBAction)openLogFolder:(id)sender +{ + NSString *tmpFolder = [[[NSProcessInfo processInfo] environment] objectForKey:@"TMPDIR"]; + [[NSWorkspace sharedWorkspace] selectFile:[tmpFolder stringByAppendingString:@"rime.squirrel.INFO"] inFileViewerRootedAtPath:tmpFolder]; +} + void show_message(const char *msg_text, const char *msg_id) { @autoreleasepool { id notification = [[NSClassFromString(@"NSUserNotification") alloc] init]; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 216546671..9493bb980 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -346,7 +346,9 @@ - (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session) { - [self commitString:[self composedString:sender]]; + NSString *composition = [self composedString:sender]; + [[NSPasteboard generalPasteboard] setString:composition forType:NSPasteboardTypeString]; + [self commitString:composition]; [self hidePalettes]; rime_get_api()->clear_composition(_session); } @@ -383,6 +385,11 @@ - (void)openWiki:(id)sender [NSApp.squirrelAppDelegate openWiki:sender]; } +- (void)openLogFolder:(id)sender +{ + [NSApp.squirrelAppDelegate openLogFolder:sender]; +} + - (NSMenu *)menu { return NSApp.squirrelAppDelegate.menu; diff --git a/zh-HK.lproj/MainMenu.strings b/zh-HK.lproj/MainMenu.strings new file mode 100644 index 000000000..2fde873e5 --- /dev/null +++ b/zh-HK.lproj/MainMenu.strings @@ -0,0 +1,18 @@ + +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "重新部署"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "檢查更新項目⋯"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "線上輔助説明⋯"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "用户設定⋯"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "同步用户資料"; + +/* Class = "NSMenuItem"; title = "Error and warning logs"; ObjectID = "809"; */ +"809.title" = "錯誤與警告記錄"; diff --git a/zh-HK.lproj/MainMenu.xib b/zh-HK.lproj/MainMenu.xib deleted file mode 100644 index c37d9596f..000000000 --- a/zh-HK.lproj/MainMenu.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zh-Hans.lproj/MainMenu.strings b/zh-Hans.lproj/MainMenu.strings new file mode 100644 index 000000000..63b2b9b18 --- /dev/null +++ b/zh-Hans.lproj/MainMenu.strings @@ -0,0 +1,18 @@ + +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "重新部署"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "检查更新…"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "在线帮助…"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "用户设置…"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "同步用户数据"; + +/* Class = "NSMenuItem"; title = "Error and warning logs"; ObjectID = "809"; */ +"809.title" = "错误和警告日志"; diff --git a/zh-Hans.lproj/MainMenu.xib b/zh-Hans.lproj/MainMenu.xib deleted file mode 100644 index cca9280d6..000000000 --- a/zh-Hans.lproj/MainMenu.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zh-Hant.lproj/MainMenu.strings b/zh-Hant.lproj/MainMenu.strings new file mode 100644 index 000000000..49408a401 --- /dev/null +++ b/zh-Hant.lproj/MainMenu.strings @@ -0,0 +1,18 @@ + +/* Class = "NSMenuItem"; title = "Deploy"; ObjectID = "774"; */ +"774.title" = "重新部署"; + +/* Class = "NSMenuItem"; title = "Check for updates…"; ObjectID = "776"; */ +"776.title" = "檢查更新項目⋯"; + +/* Class = "NSMenuItem"; title = "Rime Wiki…"; ObjectID = "797"; */ +"797.title" = "線上輔助說明⋯"; + +/* Class = "NSMenuItem"; title = "Settings…"; ObjectID = "802"; */ +"802.title" = "使用者設定⋯"; + +/* Class = "NSMenuItem"; title = "Sync user data"; ObjectID = "804"; */ +"804.title" = "同步使用者資料"; + +/* Class = "NSMenuItem"; title = "Error and warning logs"; ObjectID = "809"; */ +"809.title" = "錯誤和警告記錄"; diff --git a/zh-Hant.lproj/MainMenu.xib b/zh-Hant.lproj/MainMenu.xib deleted file mode 100644 index 532085c75..000000000 --- a/zh-Hant.lproj/MainMenu.xib +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 75b926ac3506b1acb6d0bb5ccc8f982b7f86c4af Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 28 Nov 2023 04:29:44 +0100 Subject: [PATCH 150/164] fix labels in Switcher --- SquirrelApplicationDelegate.m | 11 +- SquirrelInputController.h | 8 +- SquirrelInputController.m | 71 +++---- SquirrelPanel.h | 3 +- SquirrelPanel.m | 363 ++++++++++++++++++++-------------- en.lproj/Localizable.strings | 2 +- input_source.m | 26 +-- main.m | 11 +- 8 files changed, 286 insertions(+), 209 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 28b706c92..243c14c32 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -93,6 +93,7 @@ void notification_handler(void *context_object, RimeSessionId session_id, if ([[app_delegate panel].optionSwitcher containsOption:@(option_name)]) { if ([[app_delegate panel].optionSwitcher updateGroupState:@(message_value) ofOption:@(option_name)]) { [app_delegate loadSchemaSpecificSettings:[app_delegate panel].optionSwitcher.schemaId]; + [app_delegate loadSchemaSpecificLabels:[app_delegate panel].optionSwitcher.schemaId]; } } if ([app_delegate enableNotifications]) { @@ -175,17 +176,19 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { } - (void)loadSchemaSpecificLabels:(NSString *)schemaId { + SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; + [defaultConfig openWithConfigId:@"default"]; if (schemaId.length == 0 || [schemaId characterAtIndex:0] == '.') { + [self.panel loadLabelConfig:defaultConfig directUpdate:YES]; + [defaultConfig close]; return; } - SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; - [defaultConfig openWithConfigId:@"default"]; SquirrelConfig *schema = [[SquirrelConfig alloc] init]; if ([schema openWithSchemaId:schemaId baseConfig:defaultConfig] && [schema hasSection:@"menu"]) { - [self.panel loadLabelConfig:schema]; + [self.panel loadLabelConfig:schema directUpdate:NO]; } else { - [self.panel loadLabelConfig:defaultConfig]; + [self.panel loadLabelConfig:defaultConfig directUpdate:NO]; } [schema close]; [defaultConfig close]; diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 875d5c298..008495619 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -13,13 +13,13 @@ typedef enum { // 0 ... 9 are ordinal digits, used as (int) index // 0x21 ... 0x7e are ASCII chars (as selection keys) // other rime keycodes (as function keys), for paging etc. - kEscape = 0xff1b, // XK_Escape - kPageUp = 0xff55, // XK_Page_Up - kPageDown = 0xff56, // XK_Page_Down + kEscape = 0xff1b, // XK_Escape + kPageUp = 0xff55, // XK_Page_Up + kPageDown = 0xff56, // XK_Page_Down kVoidSymbol = 0xffffff // XK_VoidSymbol } rimeIndex; -- (void)perform:(rimeAction)action +- (void)perform:(rimeAction)action onIndex:(rimeIndex)index; @end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 9493bb980..7414aa1e0 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -19,6 +19,8 @@ - (void)rimeUpdate; @implementation SquirrelInputController { NSMutableAttributedString *_preeditString; + NSMutableString *_originalString; + NSMutableString *_composedString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; @@ -52,7 +54,6 @@ - (BOOL)handleEvent:(NSEvent *)event // Key processing will not continue in that case. In other words the // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @@ -64,13 +65,6 @@ - (BOOL)handleEvent:(NSEvent *)event } } - NSString *app = [sender bundleIdentifier]; - - if (![_currentApp isEqualToString:app]) { - _currentApp = [app copy]; - [self updateAppOptions]; - } - switch (event.type) { case NSEventTypeFlagsChanged: { if (_lastModifier == modifiers) { @@ -305,10 +299,6 @@ - (void)activateServer:(id)sender if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } - - if (!_session || !RimeFindSession(_session)) { - _session = RimeCreateSession(); - } [super activateServer:sender]; } @@ -320,6 +310,8 @@ - (instancetype)initWithServer:(IMKServer *)server if (self = [super initWithServer:server delegate:delegate client:inputClient]) { [self createSession]; _preeditString = [[NSMutableAttributedString alloc] init]; + _originalString = [[NSMutableString alloc] init]; + _composedString = [[NSMutableString alloc] init]; } return self; } @@ -346,9 +338,7 @@ - (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session) { - NSString *composition = [self composedString:sender]; - [[NSPasteboard generalPasteboard] setString:composition forType:NSPasteboardTypeString]; - [self commitString:composition]; + [self commitString:[self composedString:sender]]; [self hidePalettes]; rime_get_api()->clear_composition(_session); } @@ -361,7 +351,6 @@ - (void)commitComposition:(id)sender - (void)deploy:(id)sender { [NSApp.squirrelAppDelegate deploy:sender]; - [self createSession]; } - (void)syncUserData:(id)sender @@ -397,18 +386,12 @@ - (NSMenu *)menu - (NSAttributedString *)originalString:(id)sender { - const char *raw_input = RimeGetInput(_session); - return [[NSAttributedString alloc] initWithString:raw_input ? @(raw_input) : @""]; + return [[NSAttributedString alloc] initWithString:_originalString]; } - (id)composedString:(id)sender { - RimeCommitComposition(_session); - RIME_STRUCT(RimeCommit, commit); - RimeGetCommit(_session, &commit); - const char *composedString = commit.text; - RimeFreeCommit(&commit); - return composedString ? @(composedString) : @""; + return _composedString; } - (NSArray *)candidates:(id)sender @@ -425,6 +408,8 @@ - (void)dealloc { [self destroySession]; _preeditString = nil; + _originalString = nil; + _composedString = nil; } - (NSRange)selectionRange @@ -444,6 +429,8 @@ - (void)commitString:(id)string replacementRange:self.replacementRange]; [_preeditString deleteCharactersInRange:NSMakeRange(0, _preeditString.length)]; + [_originalString deleteCharactersInRange:NSMakeRange(0, _originalString.length)]; + [_composedString deleteCharactersInRange:NSMakeRange(0, _composedString.length)]; } - (void)cancelComposition @@ -463,17 +450,10 @@ - (void)showPreeditString:(NSString *)preedit selRange:(NSRange)range caretPos:(NSUInteger)pos { + //NSLog(@"showPreeditString: '%@'", preedit); if (_inlinePlaceHolder && _candidates.count > 0 && preedit.length == 0) { preedit = @" "; } - //NSLog(@"showPreeditString: '%@'", preedit); - if ([preedit isEqualToString:_preeditString.string] && - NSEqualRanges(range, _selRange) && pos == _caretPos) { - if (_inlinePlaceHolder) { - [self updateComposition]; - } - return; - } _selRange = range; _caretPos = pos; //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", @@ -486,9 +466,14 @@ - (void)showPreeditString:(NSString *)preedit [_preeditString addAttributes:attrs range:convertedRange]; } if (range.location < pos) { - attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:range]; + attrs = [self markForStyle:kTSMHiliteSelectedConvertedText atRange:range]; [_preeditString addAttributes:attrs range:range]; } + if (NSMaxRange(range) < preedit.length) { + NSRange rawRange = NSMakeRange(NSMaxRange(range), preedit.length - NSMaxRange(range)); + attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; + [_preeditString addAttributes:attrs range:rawRange]; + } [self updateComposition]; } @@ -570,6 +555,9 @@ - (void)createSession _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; } } + _lastModifier = 0; + _lastEventCount = 0; + [self rimeUpdate]; } } @@ -638,7 +626,7 @@ - (void)rimeUpdate RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style - if ((!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { + if (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id)) { _schemaId = @(status.schema_id); _showingSwitcherMenu = rime_get_api()->get_option(_session, "dumb"); if (!_showingSwitcherMenu) { @@ -670,6 +658,21 @@ - (void)rimeUpdate const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; + // update composed string + if (!preedit || _showingSwitcherMenu) { + [_composedString deleteCharactersInRange:NSMakeRange(0, _composedString.length)]; + } else if (rime_get_api()->get_option(_session, "soft_cursor")) { + size_t cursorPos = (size_t)ctx.composition.cursor_pos - (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0); + char composed[strlen(preedit) - 2]; + for (size_t i = 0; i < strlen(preedit) - 3; ++i) { + composed[i] = preedit[i < cursorPos ? i : i + 3]; + } + composed[strlen(preedit) - 3] = '\0'; + [_composedString setString:[@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]]; + } else { + [_composedString setString:[@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]]; + } + NSUInteger start = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; NSUInteger end = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 928d4ab52..e0849c533 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -46,6 +46,7 @@ typedef enum { - (void)loadConfig:(SquirrelConfig *)config forAppearance:(SquirrelAppear)appear; -- (void)loadLabelConfig:(SquirrelConfig *)config; +- (void)loadLabelConfig:(SquirrelConfig *)config + directUpdate:(BOOL)update; @end diff --git a/SquirrelPanel.m b/SquirrelPanel.m index e17b16e2e..89ff610af 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -159,7 +159,8 @@ - (CGFloat)annotateRubyInRange:(NSRange)range CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), kCFBooleanFalse}; CFDictionaryRef rubyAttrs = CFDictionaryCreate(NULL, keys, values, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, kCTRubyOverhangAuto, kCTRubyPositionBefore, rubyString, rubyAttrs); + CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, + kCTRubyOverhangAuto, kCTRubyPositionBefore, rubyString, rubyAttrs); [self deleteCharactersInRange:[result rangeAtIndex:3]]; if (@available(macOS 12.0, *)) { @@ -233,6 +234,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; @property(nonatomic, strong, readonly) NSString *selectKeys; +@property(nonatomic, strong, readonly) NSString *candidateFormat; @property(nonatomic, strong, readonly) NSArray *labels; @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; @@ -280,10 +282,13 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; - (void)setSelectKeys:(NSString *)selectKeys - labels:(NSArray *)labels; + labels:(NSArray *)labels + directUpdate:(BOOL)update; - (void)setCandidateFormat:(NSString *)candidateFormat; +- (void)updateCandidateFormats; + - (void)setStatusMessageType:(NSString *)statusMessageType; - (void)setAnnotationHeight:(CGFloat)height; @@ -523,23 +528,33 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle } - (void)setSelectKeys:(NSString *)selectKeys - labels:(NSArray *)labels { + labels:(NSArray *)labels + directUpdate:(BOOL)update { _selectKeys = selectKeys; _labels = labels; + if (update && _candidateFormat) { + [self updateCandidateFormats]; + } } - (void)setCandidateFormat:(NSString *)candidateFormat { + _candidateFormat = candidateFormat; + [self updateCandidateFormats]; +} + +- (void)updateCandidateFormats { // validate candidate format: must have enumerator '%c' before candidate '%@' + NSMutableString *candidateFormat = [_candidateFormat mutableCopy]; if (![candidateFormat containsString:@"%@"]) { - candidateFormat = [candidateFormat stringByAppendingString:@"%@"]; + [candidateFormat appendString:@"%@"]; } if (![candidateFormat containsString:@"%c"]) { - candidateFormat = [@"%c" stringByAppendingString:candidateFormat]; + [candidateFormat insertString:@"%c" atIndex:0]; } NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; NSRange labelRange = [candidateFormat rangeOfString:@"%c"]; if (labelRange.location > candidateRange.location) { - candidateFormat = kDefaultCandidateFormat; + [candidateFormat setString:kDefaultCandidateFormat]; candidateRange = [candidateFormat rangeOfString:@"%@"]; } labelRange = NSMakeRange(0, candidateRange.location); @@ -611,8 +626,8 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange CGContextScaleCTM(context, 1.0, -1.0); NSUInteger glyphIndex = glyRange.location; NSRect lineRect = [self lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL withoutAdditionalLayout:YES]; - CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) - [self.textStorage attributedSubstringFromRange:range]); + CTLineRef line = CTLineCreateWithAttributedString( + (CFAttributedStringRef)[self.textStorage attributedSubstringFromRange:range]); CFArrayRef runs = CTLineGetGlyphRuns(line); for (CFIndex i = 0; i < CFArrayGetCount(runs); ++i) { CGPoint position = [self locationForGlyphAtIndex:glyphIndex]; @@ -677,11 +692,15 @@ @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { + BOOL vertical = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; NSArray *lineFragments = self.textLineFragments; for (NSTextLineFragment *lineFrag in lineFragments) { - NSFont *refFont = [lineFrag.attributedString attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; + NSFont *refFont = [lineFrag.attributedString + attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 + effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; CGPoint renderOrigin = CGPointMake(point.x + NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, - point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + refFont.descender); + point.y + NSMidY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + + (vertical ? 0.0 : refFont.ascender / 2 + refFont.descender / 2)); [lineFrag drawAtPoint:renderOrigin inContext:context]; } } @@ -959,7 +978,10 @@ static inline BOOL nearEmptyRect(NSRect rect) { // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is the complete line fragment in the middle if the range spans no less than one full line -- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)leadingRect bodyRect:(NSRectPointer)bodyRect trailingRect:(NSRectPointer)trailingRect { +- (void)multilineRectForRange:(NSRange)charRange + leadingRect:(NSRectPointer)leadingRect + bodyRect:(NSRectPointer)bodyRect + trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromCharRange:charRange]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:charRange.location effectiveRange:NULL] lineSpacing]; @@ -1103,7 +1125,8 @@ - (void)updateLayer { SquirrelTheme *theme = self.currentTheme; NSRect backgroundRect = self.bounds; - NSRect textContainerRect = NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height); + NSRect textContainerRect = [self backingAlignedRect:NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height) + options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; NSRange visibleRange; if (@available(macOS 12.0, *)) { @@ -1114,7 +1137,8 @@ - (void)updateLayer { visibleRange = [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange actualGlyphRange:NULL]; } NSRange preeditRange = NSIntersectionRange(_preeditRange, visibleRange); - NSRange candidateBlockRange = NSIntersectionRange(NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue), visibleRange); + NSRange candidateBlockRange = NSIntersectionRange(NSUnionRange(_candidateRanges.firstObject.rangeValue, + theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue), visibleRange); NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange); NSRect preeditRect = NSZeroRect; @@ -1129,14 +1153,12 @@ - (void)updateLayer { if (candidateBlockRange.length > 0) { candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace / 2); if (preeditRange.length == 0) { - candidateBlockRect.origin.y += theme.linespace / 2; + candidateBlockRect.origin = textContainerRect.origin; } } if (pagingRange.length > 0) { pagingLineRect = [self contentRectForRange:pagingRange]; - if (theme.linear) { - pagingLineRect = NSOffsetRect(pagingLineRect, _insets.left, theme.edgeInset.height); - } else { + if (!theme.linear) { pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; } @@ -1147,6 +1169,8 @@ - (void)updateLayer { if (preeditRange.length > 0) { preeditRect.size.width = textContainerRect.size.width; preeditRect.origin = textContainerRect.origin; + preeditRect = [self backingAlignedRect:preeditRect + options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; // Draw highlighted part of preedit text NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { @@ -1158,25 +1182,27 @@ - (void)updateLayer { NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), innerBox); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), innerBox); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSIntersectionRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), innerBox); + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(leadingRect, innerBox) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(bodyRect, innerBox) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(trailingRect, innerBox) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; NSArray *highlightedPreeditPoints; NSArray *highlightedPreeditPoints2; // Handles the special case where containing boxes are separated - if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) - && NSMaxX(trailingRect) < NSMinX(leadingRect)) { + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && + NSMaxX(trailingRect) < NSMinX(leadingRect)) { highlightedPreeditPoints = rectVertex(leadingRect); highlightedPreeditPoints2 = rectVertex(trailingRect); } else { highlightedPreeditPoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } - highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); - if (highlightedPreeditPoints2.count > 0) { - [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; + highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, + MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); + if (highlightedPreeditPoints2) { + [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, + MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; } } } @@ -1185,8 +1211,8 @@ - (void)updateLayer { if (candidateBlockRange.length > 0) { candidateBlockRect.size.width = textContainerRect.size.width; candidateBlockRect.origin.x = textContainerRect.origin.x; - candidateBlockRect.origin.y += textContainerRect.origin.y; - candidateBlockRect = NSIntersectionRect(candidateBlockRect, textContainerRect); + candidateBlockRect = [self backingAlignedRect:NSIntersectionRect(candidateBlockRect, textContainerRect) + options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.highlightedCornerRadius); // Draw candidate highlight rect @@ -1206,25 +1232,19 @@ - (void)updateLayer { NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(leadingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(bodyRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect - : NSInsetRect(NSOffsetRect(trailingRect, _insets.left, theme.edgeInset.height), -theme.separatorWidth / 2, 0); - if (preeditRange.length == 0) { - leadingRect.origin.y += theme.linespace / 2; - bodyRect.origin.y += theme.linespace / 2; - trailingRect.origin.y += theme.linespace / 2; - } + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -theme.separatorWidth / 2, 0); + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -theme.separatorWidth / 2, 0); + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -theme.separatorWidth / 2, 0); if (!NSIsEmptyRect(leadingRect)) { leadingRect.origin.y -= theme.linespace / 2; leadingRect.size.height += theme.linespace / 2; - leadingRect = NSIntersectionRect(leadingRect, candidateBlockRect); + leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } if (!NSIsEmptyRect(trailingRect)) { trailingRect.size.height += theme.linespace / 2; - trailingRect = NSIntersectionRect(trailingRect, candidateBlockRect); + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } if (!NSIsEmptyRect(bodyRect)) { if (NSIsEmptyRect(leadingRect)) { @@ -1234,7 +1254,8 @@ - (void)updateLayer { if (NSIsEmptyRect(trailingRect)) { bodyRect.size.height += theme.linespace / 2; } - bodyRect = NSIntersectionRect(bodyRect, candidateBlockRect); + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } if (theme.tabled) { CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); @@ -1246,22 +1267,30 @@ - (void)updateLayer { } CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar - [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius)]; - [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.highlightedCornerRadius)]; + [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius / 2)]; + [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.highlightedCornerRadius / 2)]; [candidateVertGridPath closePath]; } - CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((tailEdge - leadOrigin.x) / tabInterval / 2) * tabInterval * 2 + leadOrigin.x; - if (i == _candidateRanges.count - 1 && pagingRange.length > 0 && tailEdge < pagingLineRect.origin.x) { - tabPosition = MIN(tabPosition, floor((pagingLineRect.origin.x - leadOrigin.x) / tabInterval) * tabInterval + leadOrigin.x); + CGFloat endEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + CGFloat tabPosition = ceil((endEdge - textContainerRect.origin.x) / tabInterval / 2) * tabInterval * 2 + textContainerRect.origin.x; + if (i == _candidateRanges.count - 1 && pagingRange.length > 0 && + bottomEdge > NSMinY(pagingLineRect) && tabPosition > NSMinX(pagingLineRect)) { + tabPosition -= tabInterval; } if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.width += tabPosition - tailEdge; + bodyRect.size.width += tabPosition - endEdge; + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } else if (NSIsEmptyRect(bodyRect)) { - trailingRect.size.width += tabPosition - tailEdge; + trailingRect.size.width += tabPosition - endEdge; + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } else { - bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); + bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), + NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); trailingRect = NSZeroRect; + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; } } NSArray *candidatePoints; @@ -1275,7 +1304,7 @@ - (void)updateLayer { candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); } NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); - if (candidatePoints2.count > 0) { + if (candidatePoints2) { [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.highlightedCornerRadius)]; } _candidatePaths[i] = candidatePath; @@ -1289,11 +1318,8 @@ - (void)updateLayer { NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); candidateRect.size.width = textContainerRect.size.width; candidateRect.origin.x = textContainerRect.origin.x; - candidateRect.origin.y += textContainerRect.origin.y; - if (preeditRange.length == 0) { - candidateRect.origin.y += theme.linespace / 2; - } - candidateRect = NSIntersectionRect(candidateRect, candidateBlockRect); + candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; NSArray *candidatePoints = rectVertex(candidateRect); NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); _candidatePaths[i] = candidatePath; @@ -1303,23 +1329,19 @@ - (void)updateLayer { // Draw paging Rect if (pagingRange.length > 0) { - NSRect pageDownRect = NSOffsetRect([self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)], - _insets.left, theme.edgeInset.height); + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; pageDownRect.size.width += theme.separatorWidth / 2; - NSRect pageUpRect = NSOffsetRect([self contentRectForRange:NSMakeRange(pagingRange.location, 1)], - _insets.left, theme.edgeInset.height); + NSRect pageUpRect = [self contentRectForRange:NSMakeRange(pagingRange.location, 1)]; pageUpRect.origin.x -= theme.separatorWidth / 2; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented if (theme.linear) { pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace / 2); pageUpRect = NSInsetRect(pageUpRect, 0.0, -theme.linespace / 2); } - if (preeditRange.length == 0) { - pageDownRect = NSOffsetRect(pageDownRect, 0.0, theme.linespace / 2); - pageUpRect = NSOffsetRect(pageUpRect, 0.0, theme.linespace / 2); - } - pageDownRect = NSIntersectionRect(pageDownRect, textContainerRect); - pageUpRect = NSIntersectionRect(pageUpRect, textContainerRect); + pageDownRect = [self backingAlignedRect:NSIntersectionRect(pageDownRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + pageUpRect = [self backingAlignedRect:NSIntersectionRect(pageUpRect, candidateBlockRect) + options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; pageDownPath = drawRoundedPolygon(rectVertex(pageDownRect), MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3)); pageUpPath = drawRoundedPolygon(rectVertex(pageUpRect), @@ -1339,7 +1361,7 @@ - (void)updateLayer { borderPath.windingRule = NSWindingRuleEvenOdd; } - // set layers + // Set layers _shape.path = [backgroundPath quartzPath]; _shape.fillColor = [[NSColor whiteColor] CGColor]; _shape.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); @@ -1381,6 +1403,8 @@ - (void)updateLayer { CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; candidateLayer.path = [candidateBlockPath quartzPath]; candidateLayer.fillColor = [theme.backgroundColor CGColor]; + candidateLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; + candidateLayer.shadowOffset = NSMakeSize(theme.preeditLinespace / 2, - theme.preeditLinespace / 2); [panelLayer addSublayer:candidateLayer]; } } @@ -1392,6 +1416,8 @@ - (void)updateLayer { highlightedPath = _candidatePaths[_highlightedIndex]; highlightedLayer.path = [highlightedPath quartzPath]; highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; + highlightedLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; + highlightedLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); CAShapeLayer *candidateMaskLayer = [[CAShapeLayer alloc] init]; candidateMaskLayer.path = [candidateBlockPath quartzPath]; candidateMaskLayer.fillColor = [[NSColor whiteColor] CGColor]; @@ -1420,6 +1446,8 @@ - (void)updateLayer { break; } pagingLayer.mask = textContainerLayer; + pagingLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; + pagingLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); [self.layer addSublayer:pagingLayer]; } if (theme.highlightedPreeditColor) { @@ -1428,6 +1456,8 @@ - (void)updateLayer { highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; highlightedPreeditLayer.mask = textContainerLayer; + highlightedPreeditLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; + highlightedPreeditLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); [self.layer addSublayer:highlightedPreeditLayer]; } } @@ -1486,7 +1516,6 @@ @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; - NSScreen *_screen; NSSize _maxSize; CGFloat _textWidthLimit; @@ -1582,6 +1611,22 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; + if (@available(macOS 12.0, *)) { + attrs[NSTrackingAttributeName] = @(1); + highlightedAttrs[NSTrackingAttributeName] = @(1); + commentAttrs[NSTrackingAttributeName] = @(1); + commentHighlightedAttrs[NSTrackingAttributeName] = @(1); + preeditAttrs[NSTrackingAttributeName] = @(1); + preeditHighlightedAttrs[NSTrackingAttributeName] = @(1); + } else { + attrs[NSKernAttributeName] = @(1); + highlightedAttrs[NSKernAttributeName] = @(1); + commentAttrs[NSKernAttributeName] = @(1); + commentHighlightedAttrs[NSKernAttributeName] = @(1); + preeditAttrs[NSKernAttributeName] = @(1); + preeditHighlightedAttrs[NSKernAttributeName] = @(1); + } + NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; @@ -1623,14 +1668,13 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { pagingParagraphStyle:pagingParagraphStyle statusParagraphStyle:statusParagraphStyle]; - [theme setSelectKeys:@"12345" - labels:@[@"1", @"2", @"3", @"4", @"5"]]; + [theme setSelectKeys:@"12345" labels:@[@"1", @"2", @"3", @"4", @"5"] directUpdate:NO]; [theme setCandidateFormat:kDefaultCandidateFormat]; } - (instancetype)init { self = [super initWithContentRect:_position - styleMask:NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless + styleMask:NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless | NSWindowStyleMaskHUDWindow | NSWindowStyleMaskUtilityWindow backing:NSBackingStoreBuffered defer:YES]; @@ -1704,7 +1748,7 @@ - (void)sendEvent:(NSEvent *)event { [_view.textStorage addAttributes:_view.currentTheme.pagingAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; cursorIndex = _lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; } - [_view drawViewWithInsets:_view.insets + [_view drawViewWithInsets:_view.insets candidateRanges:_view.candidateRanges highlightedIndex:_view.highlightedIndex preeditRange:_view.preeditRange @@ -1755,20 +1799,8 @@ - (void)sendEvent:(NSEvent *)event { } } -- (void)getCurrentScreen { - _screen = [NSScreen mainScreen]; - NSArray *screens = [NSScreen screens]; - for (NSUInteger i = 0; i < screens.count; ++i) { - if (NSPointInRect(_position.origin, [screens[i] frame])) { - _screen = screens[i]; - return; - } - } -} - - (void)getTextWidthLimit { - [self getCurrentScreen]; - NSRect screenRect = _screen.visibleFrame; + NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; SquirrelTheme *theme = _view.currentTheme; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - theme.separatorWidth - theme.edgeInset.width * 2; @@ -1795,7 +1827,7 @@ - (void)show { NSTextContainer *textContainer = _view.textView.textContainer; NSEdgeInsets insets = _view.insets; CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - NSRect screenRect = _screen.visibleFrame; + NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position @@ -1913,11 +1945,11 @@ - (void)show { // rotate the view, the core in vertical mode! if (theme.vertical) { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; + [self setFrame:[[NSScreen mainScreen] backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; [self.contentView setBoundsRotation:-90.0]; [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { - [self setFrame:[_screen backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; + [self setFrame:[[NSScreen mainScreen] backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; [self.contentView setBoundsRotation:0.0]; [self.contentView setBoundsOrigin:NSZeroPoint]; } @@ -1926,12 +1958,13 @@ - (void)show { NSWidth(frameRect) - insets.left - insets.right, NSHeight(frameRect) - insets.top - insets.bottom); [_view.textView setBoundsRotation:0.0]; - [_view setBoundsOrigin:NSZeroPoint]; + [_view setBoundsOrigin:NSMakePoint(-insets.left, -insets.top)]; [_view.textView setBoundsOrigin:NSZeroPoint]; [_view setFrame:frameRect]; [_view.textView setFrame:textFrameRect]; if (theme.translucency > 0) { + [_back setBoundsOrigin:NSMakePoint(-insets.left, -insets.top)]; [_back setFrame:frameRect]; [_back setAppearance:NSApp.effectiveAppearance]; [_back setHidden:NO]; @@ -1961,36 +1994,35 @@ - (void)setLayoutForRange:(NSRange)charRange { NSFont *refFont = [_view.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:charRange.location effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - NSParagraphStyle *style = [_view.textStorage attribute:NSParagraphStyleAttributeName - atIndex:charRange.location effectiveRange:NULL]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; if (@available(macOS 12.0, *)) { [_view.textStorage enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue] + lineHeight / 2 - refFontHeight / 2; + CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue]; NSFont *runFont = attrs[NSFontAttributeName]; NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; - if (superscript != 0) { - baselineOffset += superscript == 1 ? runFont.ascender + runFont.descender : -runFont.descender; - } if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { - if (!verticalLayout) { - baselineOffset -= (runFont.ascender - runFont.descender) / 16; - } else if (superscript == 1) { - baselineOffset -= runFont.ascender + runFont.descender - (runFont.ascender - runFont.descender) / 16; + if (verticalLayout) { + baselineOffset -= superscript * (runFont.ascender - runFont.descender) / 16; } else if (superscript == -1) { - baselineOffset += runFont.descender - (runFont.ascender - runFont.descender) / 16; + baselineOffset -= runFont.descender; + } else if (superscript == 1) { + baselineOffset -= runFont.underlinePosition; } + } else if (superscript != 0) { + baselineOffset -= runFont.descender; } [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset) range:range]; }]; } else { + NSParagraphStyle *style = [_view.textStorage attribute:NSParagraphStyleAttributeName + atIndex:charRange.location effectiveRange:NULL]; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; NSLayoutManager *layoutManager = _view.textView.layoutManager; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange @@ -2004,6 +2036,7 @@ - (void)setLayoutForRange:(NSRange)charRange { NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; NSDictionary *attrs = [layoutManager.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; NSFont *runFont = attrs[NSFontAttributeName]; + NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; @@ -2012,23 +2045,24 @@ - (void)setLayoutForRange:(NSRange)charRange { NSNumber *verticalGlyph = attrs[NSVerticalGlyphFormAttributeName]; if (verticalGlyph ? verticalGlyph.boolValue : YES) { runFont = runFont.verticalFont; + resizedRefFont = resizedRefFont.verticalFont; } } if (superscript != 0) { - offset += superscript == 1 ? refFont.ascender - runFont.ascender : refFont.descender - runFont.descender; + offset += superscript == 1 ? refFont.ascender - resizedRefFont.ascender : refFont.descender - resizedRefFont.descender; if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { - offset += superscript == 1 ? runFont.pointSize / 16 : runFont.descender + runFont.pointSize * 219 / 800; + offset -= (runFont.ascender - runFont.descender) / 16; } } if (verticalLayout) { if ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassRoman)] || !runFont.vertical) { - runGlyphPosition.y = alignment - offset + refFont.xHeight / 2; + runGlyphPosition.y = alignment - offset + resizedRefFont.xHeight / 2; } else { - runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] && superscript == 0 ? runFont.pointSize / 16 : 0.0); - runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? runFont.pointSize * 119 / 800 : 0.0; + runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] && superscript == 0 ? (runFont.ascender - runFont.descender) / 16 : 0.0); + runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFont.ascender - runFont.descender) / 8 : 0.0; } } else { - runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - refFont.xHeight / 2 : 0.0); + runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - resizedRefFont.xHeight / 2 : 0.0); } [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; j = NSMaxRange(runRange); @@ -2037,7 +2071,7 @@ - (void)setLayoutForRange:(NSRange)charRange { } } -- (BOOL)shouldBreakLineBeforeRange:(NSRange)range { +- (BOOL)shouldBreakLineInsideRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; NSUInteger __block lineCount = 0; if (@available(macOS 12.0, *)) { @@ -2046,8 +2080,8 @@ - (BOOL)shouldBreakLineBeforeRange:(NSRange)range { enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { - ++lineCount; + usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + lineCount += 1 + (NSMaxX(segFrame) > _textWidthLimit); return YES; }]; } else { @@ -2055,7 +2089,7 @@ - (BOOL)shouldBreakLineBeforeRange:(NSRange)range { actualCharacterRange:NULL]; [_view.textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { - ++lineCount; + lineCount += 1 + (NSMaxX(usedRect) > self->_textWidthLimit); }]; } return lineCount > 1; @@ -2065,25 +2099,45 @@ - (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLeng [_view.textStorage fixFontAttributeInRange:range]; if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; - CGFloat __block rangeEdge; + CGFloat __block rangeEndEdge; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange type:NSTextLayoutManagerSegmentTypeStandard options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { - rangeEdge = NSMaxX(segFrame); + rangeEndEdge = NSMaxX(segFrame); return YES; }]; [_view.textView.textLayoutManager ensureLayoutForRange:_view.textView.textContentStorage.documentRange]; NSRect container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; - *maxLineLength = MAX(MIN(_textWidthLimit, NSWidth(container)), _maxSize.width); - return NSMinX(container) + *maxLineLength > rangeEdge; + *maxLineLength = MAX(MIN(_textWidthLimit, NSMaxX(container)), _maxSize.width); + return *maxLineLength > rangeEndEdge; } else { NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; - CGFloat rangeEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); + CGFloat rangeEndEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); NSRect container = [_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer]; - *maxLineLength = MAX(MIN(_textWidthLimit, NSWidth(container)), _maxSize.width); - return NSMinX(container) + *maxLineLength > rangeEdge; + *maxLineLength = MAX(MIN(_textWidthLimit, NSMaxX(container)), _maxSize.width); + return *maxLineLength > rangeEndEdge; + } +} + +- (CGFloat)getInlineOffsetAfterCharacterRange:(NSRange)range { + if (@available(macOS 12.0, *)) { + NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; + CGFloat __block offset; + [_view.textView.textLayoutManager + enumerateTextSegmentsInRange:textRange + type:NSTextLayoutManagerSegmentTypeStandard + options:NSTextLayoutManagerSegmentOptionsUpstreamAffinity + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + offset = NSMaxX(segFrame); + return NO; + }]; + return offset; + } else { + NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; + NSRect boundingRect = [_view.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_view.textView.textContainer]; + return NSMaxX(boundingRect); } } @@ -2221,15 +2275,15 @@ - (void)showPreedit:(NSString *)preedit NSUInteger separatorStart = text.length; // separator: linear = " "; tabled = " \t"; stacked = "\n" NSMutableAttributedString *separator = [theme.separator mutableCopy]; - if (theme.tabled) { // fill gaps to make cells 2^n tabs wide - CGFloat widthInTabs = (ceil([text attributedSubstringFromRange:candidateRanges.lastObject.rangeValue].size.width) + theme.separatorWidth) / tabInterval; - NSUInteger numPaddingTabs = (NSUInteger)(ceil(widthInTabs / 2) * 2 - ceil(widthInTabs)); + if (theme.tabled) { // fill gaps to make cells 2N-tab wide + CGFloat widthInTabs = ([self getInlineOffsetAfterCharacterRange:candidateRanges.lastObject.rangeValue] + theme.separatorWidth) / tabInterval; + NSUInteger numPaddingTabs = (NSUInteger)ceil(ceil(widthInTabs / 2) * 2 - widthInTabs) - 1; [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; } [text appendAttributedString:separator]; [text appendAttributedString:item]; if (theme.linear && (ceil(item.size.width + theme.separatorWidth) > _textWidthLimit || - [self shouldBreakLineBeforeRange:NSMakeRange(lineStart, text.length - lineStart)])) { + [self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)])) { [text replaceCharactersInRange:NSMakeRange(separatorStart + 1, separator.length - 1) withString:@"\n"]; lineStart = separatorStart + 2; } @@ -2265,7 +2319,7 @@ - (void)showPreedit:(NSString *)preedit CGFloat maxLineLength; [text appendAttributedString:paging]; if (theme.linear) { - if ([self shouldBreakLineBeforeRange:NSMakeRange(lineStart, text.length - lineStart)]) { + if ([self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)]) { [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 0) withString:@"\n"]; lineStart = pagingStart; pagingStart += 1; @@ -2274,13 +2328,15 @@ - (void)showPreedit:(NSString *)preedit [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; paragraphStyleCandidate.tabStops = @[]; - CGFloat candidateEndPosition = ceil([text attributedSubstringFromRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)].size.width); + CGFloat candidateEndPosition = ceil([self getInlineOffsetAfterCharacterRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)]); CGFloat textPostion = tabInterval; - while (textPostion < candidateEndPosition) { - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:textPostion]]; + while (textPostion <= candidateEndPosition) { + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft + location:textPostion options:@{}]]; textPostion += tabInterval; } - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithType:NSRightTabStopType location:_textWidthLimit]]; + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate @@ -2290,8 +2346,10 @@ - (void)showPreedit:(NSString *)preedit if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { [text replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1) withString:@"\t"]; [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) withString:@"\t"]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithType:NSCenterTabStopType location:maxLineLength / 2], - [[NSTextTab alloc] initWithType:NSRightTabStopType location:maxLineLength]]; + paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter + location:maxLineLength / 2 options:@{}], + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging @@ -2514,39 +2572,44 @@ - (void)setAnnotationHeight:(CGFloat)height { [[_view selectTheme:YES] setAnnotationHeight:height]; } -- (void)loadLabelConfig:(SquirrelConfig *)config { +- (void)loadLabelConfig:(SquirrelConfig *)config + directUpdate:(BOOL)update { SquirrelTheme *theme = [_view selectTheme:defaultAppear]; - [SquirrelPanel updateTheme:theme withLabelConfig:config]; + [SquirrelPanel updateTheme:theme withLabelConfig:config directUpdate:update]; SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; - [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; + [SquirrelPanel updateTheme:darkTheme withLabelConfig:config directUpdate:update]; } + (void)updateTheme:(SquirrelTheme *)theme - withLabelConfig:(SquirrelConfig *)config { + withLabelConfig:(SquirrelConfig *)config + directUpdate:(BOOL)update { NSUInteger menuSize = (NSUInteger)[config getInt:@"menu/page_size"] ? : 5; NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; - if (selectKeys) { - NSString *keyCaps = [[selectKeys uppercaseString] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; + if (selectLabels) { for (NSUInteger i = 0; i < menuSize; ++i) { - labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; + labels[i] = selectLabels[i]; } - } else { - selectKeys = [@"1234567890" substringToIndex:menuSize]; - NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; - if (selectLabels) { + } + if (selectKeys) { + if (!selectLabels) { + NSString *keyCaps = [[selectKeys uppercaseString] + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { - labels[i] = selectLabels[i]; + labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; } - } else { + } + } else { + selectKeys = [@"1234567890" substringToIndex:menuSize]; + if (!selectLabels) { NSString *numerals = [selectKeys stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; } } } - [theme setSelectKeys:selectKeys labels:labels]; + [theme setSelectKeys:selectKeys labels:labels directUpdate:update]; } - (void)loadConfig:(SquirrelConfig *)config diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index f18edf7fd..13f2f14e1 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -6,6 +6,6 @@ "deploy_failure" = "Error occurred. See log file $TMPDIR/rime.squirrel.INFO."; "problematic_launch" = "Problematic launch detected! \ - Squirrel may be suffering a crash due to imporper configuration. \ + Squirrel may be suffering a crash due to improper configurations. \ Revert previous modifications to see if the problem recurs."; "say_voice" = "Alex"; diff --git a/input_source.m b/input_source.m index 2d4a0cab0..3c5572ff3 100644 --- a/input_source.m +++ b/input_source.m @@ -10,9 +10,11 @@ static CFStringRef kCantInputModeID = CFSTR("im.rime.inputmethod.Squirrel.Cant"); -#define HANS_INPUT_MODE (1 << 0) -#define HANT_INPUT_MODE (1 << 1) -#define CANT_INPUT_MODE (1 << 2) +typedef enum { + HANS_INPUT_MODE = 1 << 0, + HANT_INPUT_MODE = 1 << 1, + CANT_INPUT_MODE = 1 << 2 +} RimeInputMode; void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( @@ -30,11 +32,11 @@ void ActivateInputSource(int enabled_modes) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if ((CFStringCompare(sourceID, kHansInputModeID, 0) == 0 && + if ((CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo && (enabled_modes & HANS_INPUT_MODE) != 0) || - (CFStringCompare(sourceID, kHantInputModeID, 0) == 0 && + (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo && (enabled_modes & HANT_INPUT_MODE) != 0) || - (CFStringCompare(sourceID, kCantInputModeID, 0) == 0 && + (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo && (enabled_modes & CANT_INPUT_MODE) != 0)) { OSStatus enableError = TISEnableInputSource(inputSource); if (enableError) { @@ -63,9 +65,9 @@ void DeactivateInputSource(void) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0 || - CFStringCompare(sourceID, kHantInputModeID, 0) == 0 || - CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { + if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo || + CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo || + CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { @@ -94,11 +96,11 @@ int GetEnabledInputModes(void) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { - if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0) { + if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo) { input_modes |= HANS_INPUT_MODE; - } else if (CFStringCompare(sourceID, kHantInputModeID, 0) == 0) { + } else if (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo) { input_modes |= HANT_INPUT_MODE; - } else if (CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { + } else if (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo) { input_modes |= CANT_INPUT_MODE; } } diff --git a/main.m b/main.m index 9651525cb..6984cda35 100644 --- a/main.m +++ b/main.m @@ -4,12 +4,17 @@ #import #import +typedef enum { + DEFAULT_INPUT_MODE = 1 << 0, + HANS_INPUT_MODE = 1 << 0, + HANT_INPUT_MODE = 1 << 1, + CANT_INPUT_MODE = 1 << 2 +} RimeInputMode; + void RegisterInputSource(void); int GetEnabledInputModes(void); void DeactivateInputSource(void); -void ActivateInputSource(int input_modes); - -#define DEFAULT_INPUT_MODE 1 +void ActivateInputSource(RimeInputMode); // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. From 5001573ffc228a8178bea87929c18689f5858fbc Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 19 Dec 2023 11:07:51 +0100 Subject: [PATCH 151/164] goodOldCapsLock --- Squirrel.xcodeproj/project.pbxproj | 14 +++++++++----- SquirrelInputController.m | 27 +++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 9d6ae439f..62c6e6f9c 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -21,7 +21,6 @@ 442C64921F7A410A0027EFBE /* rime-install in CopyFiles */ = {isa = PBXBuildFile; fileRef = 442C64901F7A404A0027EFBE /* rime-install */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 4443A83A1828CC5100731305 /* input_source.m in Sources */ = {isa = PBXBuildFile; fileRef = 4443A8391828CC5100731305 /* input_source.m */; }; 446C01D71F767BD400A6C23E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 446C01D61F767BD400A6C23E /* Assets.xcassets */; }; - 447765C925C30E97002415AF /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 447765C725C30E6B002415AF /* Sparkle.framework */; }; 447765CA25C30E97002415AF /* Sparkle.framework in Copy 3rd-party Frameworks */ = {isa = PBXBuildFile; fileRef = 447765C725C30E6B002415AF /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 448363DD25BDBBED0022C7BA /* pinyin.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 448363D925BDBBBF0022C7BA /* pinyin.yaml */; }; 448363DE25BDBBED0022C7BA /* zhuyin.yaml in Copy Shared Support Files */ = {isa = PBXBuildFile; fileRef = 448363DA25BDBBBF0022C7BA /* zhuyin.yaml */; }; @@ -81,6 +80,7 @@ F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F478A3572B1078D200D7794A /* QuartzCore.framework */; }; F478A35B2B10795800D7794A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97324FDCFA39411CA2CEA /* AppKit.framework */; }; F478A35C2B10795B00D7794A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; }; + F48CFB692B326F4900DB9CF9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 447765C725C30E6B002415AF /* Sparkle.framework */; }; F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */; }; F4E457BA2AD97412003F6D79 /* t2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A92AD97411003F6D79 /* t2emoji.json */; }; F4E457BB2AD97412003F6D79 /* hk2t.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AA2AD97411003F6D79 /* hk2t.json */; }; @@ -98,6 +98,7 @@ F4E457C72AD97412003F6D79 /* EmojiCategoryT.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */; }; F4E457C82AD97412003F6D79 /* hk2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B72AD97412003F6D79 /* hk2emoji.json */; }; F4E457C92AD97412003F6D79 /* tw2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457B82AD97412003F6D79 /* tw2emoji.json */; }; + F4EC47AA2B3239A2004862A4 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4EC47A92B323999004862A4 /* IOKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -320,6 +321,7 @@ F4E457B62AD97412003F6D79 /* EmojiCategoryT.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryT.ocd2; sourceTree = ""; }; F4E457B72AD97412003F6D79 /* hk2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = hk2emoji.json; sourceTree = ""; }; F4E457B82AD97412003F6D79 /* tw2emoji.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tw2emoji.json; sourceTree = ""; }; + F4EC47A92B323999004862A4 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -327,13 +329,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */, - F478A3592B1078F500D7794A /* Cocoa.framework in Frameworks */, - E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */, F478A35B2B10795800D7794A /* AppKit.framework in Frameworks */, A4B8E1B30F645B870094E08B /* Carbon.framework in Frameworks */, + F478A3592B1078F500D7794A /* Cocoa.framework in Frameworks */, F478A35C2B10795B00D7794A /* Foundation.framework in Frameworks */, - 447765C925C30E97002415AF /* Sparkle.framework in Frameworks */, + E93074B70A5C264700470842 /* InputMethodKit.framework in Frameworks */, + F4EC47AA2B3239A2004862A4 /* IOKit.framework in Frameworks */, + F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */, + F48CFB692B326F4900DB9CF9 /* Sparkle.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -377,6 +380,7 @@ F478A3582B1078D600D7794A /* Cocoa.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, E93074B60A5C264700470842 /* InputMethodKit.framework */, + F4EC47A92B323999004862A4 /* IOKit.framework */, F478A3572B1078D200D7794A /* QuartzCore.framework */, ); name = "Other Frameworks"; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 7414aa1e0..7938b61b6 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -6,6 +6,9 @@ #import "macos_keycode.h" #import #import +#import +#import +#import @interface SquirrelInputController (Private) - (void)createSession; @@ -31,6 +34,7 @@ @implementation SquirrelInputController { BOOL _inlinePreedit; BOOL _inlineCandidate; BOOL _showingSwitcherMenu; + BOOL _goodOldCapsLock; // app-specific bug fix BOOL _inlinePlaceHolder; BOOL _panellessCommitFix; @@ -152,7 +156,19 @@ - (BOOL)handleEvent:(NSEvent *)event return handled; } -- (BOOL)processKey:(int)rime_keycode +void set_caps_lock_led(bool target_state) { + io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass)); + io_connect_t ioConnect = 0; + IOServiceOpen(ioService, mach_task_self_, kIOHIDParamConnectType, &ioConnect); + bool current_state = false; + IOHIDGetModifierLockState(ioConnect, kIOHIDCapsLockState, ¤t_state); + if (current_state != target_state) { + IOHIDSetModifierLockState(ioConnect, kIOHIDCapsLockState, target_state); + } + IOServiceClose(ioConnect); +} + +- (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers { // with linear candidate list, arrow keys may behave differently. @@ -299,6 +315,13 @@ - (void)activateServer:(id)sender if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } + + SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; + if ([defaultConfig openWithConfigId:@"default"] && [defaultConfig hasSection:@"ascii_composer"]) { + _goodOldCapsLock = [defaultConfig getBool:@"ascii_composer/good_old_caps_lock"]; + } + [defaultConfig close]; + [super activateServer:sender]; } @@ -492,7 +515,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit [self.client attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view - if (_lastModifier & NSEventModifierFlagCapsLock) { + if (_goodOldCapsLock && _lastModifier & NSEventModifierFlagCapsLock) { NSRect screenRect = [[NSScreen mainScreen] frame]; if (NSIntersectsRect(inputPos, screenRect)) { screenRect = [[NSScreen mainScreen] visibleFrame]; From d0bbd802dbc59263c0d775af4aab78b87699cede Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 20 Dec 2023 11:13:10 +0100 Subject: [PATCH 152/164] choose/hilite --- SquirrelInputController.h | 6 +++--- SquirrelInputController.m | 22 +++++++++++++++------- SquirrelPanel.m | 6 ++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 008495619..54f007db9 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -4,9 +4,9 @@ @interface SquirrelInputController : IMKInputController typedef enum { - kSELECT = 1, // accepts indices in both digits and selection keys - kDELETE = 2, // only accepts indices in digits, e.g. (int) 1 - kCHOOSE = 3 // only accepts indices in selection keys, e.g. (char) '1' / 'A' + kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape) + kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A') + kDELETE = 3 // only accepts indices in digits (int 1) } rimeAction; typedef enum { diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 7938b61b6..6c6501cce 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -219,14 +219,22 @@ - (BOOL)processKey:(int)rime_keycode - (void)perform:(rimeAction)action onIndex:(rimeIndex)index { bool handled = false; - if (action == kSELECT && ((index >= '!' && index <= '~') || - index == kPageUp || index == kPageDown || index == kEscape)) { + if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) { + handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0); + } else if ((index == kPageUp || index == kPageDown || index == kEscape) && action == kSELECT) { handled = rime_get_api()->process_key(_session, (int)index, 0); - } else if (action == kCHOOSE && index >= '!' && index <= '~') { - handled = rime_get_api()->process_key(_session, (int)index, kAltMask); - } else if (action == kDELETE && index >= 0 && index < 10) { - // kDELETE takes ordinal digits (instead of characters) as indexes - handled= rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index); + } else if (index >= 0 && index < 10) { + switch (action) { + case kDELETE: + handled = rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index); + break; + case kSELECT: + handled = rime_get_api()->select_candidate_on_current_page(_session, (size_t)index); + break; + case kHILITE: + handled = rime_get_api()->hilite_candidate_on_current_page(_session, (size_t)index); + break; + } } if (handled) { [self rimeUpdate]; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 89ff610af..e798285f9 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1713,8 +1713,7 @@ - (void)sendEvent:(NSEvent *)event { case NSEventTypeLeftMouseUp: if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex == _index) { - rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; - [_inputController perform:kSELECT onIndex:indexChar]; + [_inputController perform:kSELECT onIndex:(rimeIndex)cursorIndex]; } else if (cursorIndex == _turnPage) { rimeIndex indexChar = cursorIndex == NSPageUpFunctionKey ? kPageUp : (cursorIndex == NSPageDownFunctionKey ? kPageDown : kVoidSymbol); @@ -1735,8 +1734,7 @@ - (void)sendEvent:(NSEvent *)event { if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { if (cursorIndex >= 0 && cursorIndex < _numCandidates && _index != cursorIndex) { _index = cursorIndex; - rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; - [_inputController perform:kCHOOSE onIndex:indexChar]; + [_inputController perform:kHILITE onIndex:(rimeIndex)cursorIndex]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; if (_turnPage == NSPageUpFunctionKey) { From 76eb8d7c0a567fc4ee80ab1a7e1ec5cd24b2518f Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 19 Dec 2023 22:58:31 +0100 Subject: [PATCH 153/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 0272f7419..8b678a7ad 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 0272f74198b4918db1232c9d91fb4061140cb98d +Subproject commit 8b678a7ad7553e8e203bbfa784f6fd78fc02a8a1 From 442b00da823ed7ebf816daa692709d5d4e125674 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 22 Dec 2023 03:20:48 +0100 Subject: [PATCH 154/164] icon --- Assets.xcassets/rime.imageset/Contents.json | 43 +++++++------ Assets.xcassets/rime.imageset/rime.fill.svg | 4 ++ Assets.xcassets/rime.imageset/rime.stroke.svg | 4 ++ Assets.xcassets/rime.imageset/rime.svg | 16 +---- .../rimeSymbol.imageset/Contents.json | 43 ------------- .../rimeSymbol.fill_Normal.png | Bin 5233 -> 0 bytes .../rimeSymbol.fill_Normal@2x.png | Bin 5406 -> 0 bytes .../rimeSymbol.stroke_Normal.png | Bin 5380 -> 0 bytes .../rimeSymbol.stroke_Normal@2x.png | Bin 5446 -> 0 bytes Base.lproj/MainMenu.xib | 57 ------------------ Info.plist | 4 +- 11 files changed, 40 insertions(+), 131 deletions(-) create mode 100644 Assets.xcassets/rime.imageset/rime.fill.svg create mode 100644 Assets.xcassets/rime.imageset/rime.stroke.svg delete mode 100644 Assets.xcassets/rimeSymbol.imageset/Contents.json delete mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal.png delete mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal@2x.png delete mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png delete mode 100644 Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png diff --git a/Assets.xcassets/rime.imageset/Contents.json b/Assets.xcassets/rime.imageset/Contents.json index 7e46fb00c..01807f061 100644 --- a/Assets.xcassets/rime.imageset/Contents.json +++ b/Assets.xcassets/rime.imageset/Contents.json @@ -1,23 +1,32 @@ { "images" : [ { - "compression-type" : "lossless", "filename" : "rime.svg", - "idiom" : "mac", - "resizing" : { - "cap-insets" : { - "bottom" : 2, - "left" : 5, - "right" : 5, - "top" : 2 - }, - "center" : { - "height" : 12, - "mode" : "tile", - "width" : 12 - }, - "mode" : "9-part" - } + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "filename" : "rime.svg", + "idiom" : "universal" + }, + { + "filename" : "rime.fill.svg", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "contrast", + "value" : "high" + } + ], + "filename" : "rime.stroke.svg", + "idiom" : "mac" } ], "info" : { @@ -25,7 +34,7 @@ "version" : 1 }, "properties" : { - "compression-type" : "lossless", + "compression-type" : "automatic", "preserves-vector-representation" : true, "template-rendering-intent" : "template" } diff --git a/Assets.xcassets/rime.imageset/rime.fill.svg b/Assets.xcassets/rime.imageset/rime.fill.svg new file mode 100644 index 000000000..3100290b0 --- /dev/null +++ b/Assets.xcassets/rime.imageset/rime.fill.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Assets.xcassets/rime.imageset/rime.stroke.svg b/Assets.xcassets/rime.imageset/rime.stroke.svg new file mode 100644 index 000000000..cbe788f0a --- /dev/null +++ b/Assets.xcassets/rime.imageset/rime.stroke.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Assets.xcassets/rime.imageset/rime.svg b/Assets.xcassets/rime.imageset/rime.svg index 1aa069ca9..a936b2cb0 100644 --- a/Assets.xcassets/rime.imageset/rime.svg +++ b/Assets.xcassets/rime.imageset/rime.svg @@ -1,14 +1,4 @@ - + - - - - - - + + diff --git a/Assets.xcassets/rimeSymbol.imageset/Contents.json b/Assets.xcassets/rimeSymbol.imageset/Contents.json deleted file mode 100644 index b670bda6b..000000000 --- a/Assets.xcassets/rimeSymbol.imageset/Contents.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "images" : [ - { - "filename" : "rimeSymbol.fill_Normal.png", - "idiom" : "mac", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "contrast", - "value" : "high" - } - ], - "filename" : "rimeSymbol.stroke_Normal.png", - "idiom" : "mac", - "scale" : "1x" - }, - { - "filename" : "rimeSymbol.fill_Normal@2x.png", - "idiom" : "mac", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "contrast", - "value" : "high" - } - ], - "filename" : "rimeSymbol.stroke_Normal@2x.png", - "idiom" : "mac", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "template" - } -} diff --git a/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal.png b/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.fill_Normal.png deleted file mode 100644 index 0886382267ba9c9743597c257059934e6baf410f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5233 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 diff --git a/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png b/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal.png deleted file mode 100644 index 5cd156c1bd3c00d0fda4c8e18de2e03244bd0fcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5380 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 V88c(s$Fl!li!`v%FVl64{Ts~LM(_Xt diff --git a/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png b/Assets.xcassets/rimeSymbol.imageset/rimeSymbol.stroke_Normal@2x.png deleted file mode 100644 index 4af72fe60cea80b943ca4b8f2ea0860a00b10db0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5446 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 - @@ -24,80 +23,32 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -107,12 +58,4 @@ - - - - - - - - diff --git a/Info.plist b/Info.plist index 6cdea36f3..5a1808ca4 100644 --- a/Info.plist +++ b/Info.plist @@ -110,8 +110,8 @@ tsVisibleInputModeOrderedArrayKey - im.rime.inputmethod.Squirrel.Hant im.rime.inputmethod.Squirrel.Hans + im.rime.inputmethod.Squirrel.Hant im.rime.inputmethod.Squirrel.Cant @@ -123,6 +123,8 @@ SquirrelInputController LSBackgroundOnly + LSHasLocalizedDisplayName + LSUIElement NSMainNibFile From 7c9012336b29e96b24304680d8a86f8142671fc1 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 30 Dec 2023 11:27:51 +0100 Subject: [PATCH 155/164] symbols --- Assets.xcassets/Symbols/Contents.json | 9 + .../Contents.json | 15 ++ .../chevron.down.circle.fill.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.down.circle.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.left.circle.fill.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.left.circle.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.right.circle.fill.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.right.circle.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../chevron.up.circle.fill.svg | 160 ++++++++++++++++++ .../chevron.up.circle.symbolset/Contents.json | 15 ++ .../chevron.up.circle.svg | 160 ++++++++++++++++++ .../Contents.json | 15 ++ .../delete.backward.fill.svg | 160 ++++++++++++++++++ .../delete.backward.symbolset/Contents.json | 15 ++ .../delete.backward.svg | 160 ++++++++++++++++++ 21 files changed, 1759 insertions(+) create mode 100644 Assets.xcassets/Symbols/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/chevron.down.circle.fill.svg create mode 100644 Assets.xcassets/Symbols/chevron.down.circle.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.down.circle.symbolset/chevron.down.circle.svg create mode 100644 Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/chevron.left.circle.fill.svg create mode 100644 Assets.xcassets/Symbols/chevron.left.circle.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.left.circle.symbolset/chevron.left.circle.svg create mode 100644 Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/chevron.right.circle.fill.svg create mode 100644 Assets.xcassets/Symbols/chevron.right.circle.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.right.circle.symbolset/chevron.right.circle.svg create mode 100644 Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/chevron.up.circle.fill.svg create mode 100644 Assets.xcassets/Symbols/chevron.up.circle.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.up.circle.symbolset/chevron.up.circle.svg create mode 100644 Assets.xcassets/Symbols/delete.backward.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg create mode 100644 Assets.xcassets/Symbols/delete.backward.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg diff --git a/Assets.xcassets/Symbols/Contents.json b/Assets.xcassets/Symbols/Contents.json new file mode 100644 index 000000000..6e965652d --- /dev/null +++ b/Assets.xcassets/Symbols/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/Contents.json new file mode 100644 index 000000000..b9f37d2d2 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.down.circle.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/chevron.down.circle.fill.svg b/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/chevron.down.circle.fill.svg new file mode 100644 index 000000000..5372bbf00 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.circle.fill.symbolset/chevron.down.circle.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.down.circle.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.down.circle.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.down.circle.symbolset/Contents.json new file mode 100644 index 000000000..429d077ea --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.circle.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.down.circle.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.down.circle.symbolset/chevron.down.circle.svg b/Assets.xcassets/Symbols/chevron.down.circle.symbolset/chevron.down.circle.svg new file mode 100644 index 000000000..3768f2b29 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.circle.symbolset/chevron.down.circle.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.down.circle + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/Contents.json new file mode 100644 index 000000000..af1cde5fa --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.left.circle.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/chevron.left.circle.fill.svg b/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/chevron.left.circle.fill.svg new file mode 100644 index 000000000..41fd99e8d --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.left.circle.fill.symbolset/chevron.left.circle.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.left.circle.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.left.circle.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.left.circle.symbolset/Contents.json new file mode 100644 index 000000000..6f8f69fc9 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.left.circle.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.left.circle.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.left.circle.symbolset/chevron.left.circle.svg b/Assets.xcassets/Symbols/chevron.left.circle.symbolset/chevron.left.circle.svg new file mode 100644 index 000000000..3829bfc3d --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.left.circle.symbolset/chevron.left.circle.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.left.circle + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/Contents.json new file mode 100644 index 000000000..2cebdf058 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.right.circle.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/chevron.right.circle.fill.svg b/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/chevron.right.circle.fill.svg new file mode 100644 index 000000000..4201be53d --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.right.circle.fill.symbolset/chevron.right.circle.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.right.circle.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.right.circle.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.right.circle.symbolset/Contents.json new file mode 100644 index 000000000..b9fd72302 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.right.circle.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.right.circle.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.right.circle.symbolset/chevron.right.circle.svg b/Assets.xcassets/Symbols/chevron.right.circle.symbolset/chevron.right.circle.svg new file mode 100644 index 000000000..042dc9b9f --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.right.circle.symbolset/chevron.right.circle.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.right.circle + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/Contents.json new file mode 100644 index 000000000..e811e05f5 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.up.circle.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/chevron.up.circle.fill.svg b/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/chevron.up.circle.fill.svg new file mode 100644 index 000000000..b03c50302 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.circle.fill.symbolset/chevron.up.circle.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.up.circle.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.up.circle.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.up.circle.symbolset/Contents.json new file mode 100644 index 000000000..f7e785481 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.circle.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.up.circle.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.up.circle.symbolset/chevron.up.circle.svg b/Assets.xcassets/Symbols/chevron.up.circle.symbolset/chevron.up.circle.svg new file mode 100644 index 000000000..bf5cb5e00 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.circle.symbolset/chevron.up.circle.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.up.circle + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/delete.backward.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/Contents.json new file mode 100644 index 000000000..009340fb1 --- /dev/null +++ b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "delete.backward.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg new file mode 100644 index 000000000..3a839c4d9 --- /dev/null +++ b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from delete.backward.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/delete.backward.symbolset/Contents.json b/Assets.xcassets/Symbols/delete.backward.symbolset/Contents.json new file mode 100644 index 000000000..b7fe9eb3b --- /dev/null +++ b/Assets.xcassets/Symbols/delete.backward.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "delete.backward.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg b/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg new file mode 100644 index 000000000..e4630ab4d --- /dev/null +++ b/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from delete.backward + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 48047d2ac4a5c5caf25a5b805a6897bd99c14f24 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 5 Jan 2024 00:51:23 +0100 Subject: [PATCH 156/164] configure --- .github/workflows/commit-ci.yml | 2 +- .github/workflows/pull-request-ci.yml | 2 +- .github/workflows/release-ci.yml | 2 +- INSTALL.md | 2 +- Info.plist | 52 +++++++++++----------- Squirrel.xcodeproj/project.pbxproj | 64 ++++++++++++++++++++++++--- autobuild.sh | 4 +- data/squirrel.yaml | 2 + 8 files changed, 92 insertions(+), 38 deletions(-) diff --git a/.github/workflows/commit-ci.yml b/.github/workflows/commit-ci.yml index 6eb251f88..a55951ede 100644 --- a/.github/workflows/commit-ci.yml +++ b/.github/workflows/commit-ci.yml @@ -5,7 +5,7 @@ on: - '*' jobs: build: - runs-on: macos-latest + runs-on: macos-14 steps: - name: Checkout last commit uses: actions/checkout@v3 diff --git a/.github/workflows/pull-request-ci.yml b/.github/workflows/pull-request-ci.yml index 2bb3f0ad0..816a98d07 100644 --- a/.github/workflows/pull-request-ci.yml +++ b/.github/workflows/pull-request-ci.yml @@ -2,7 +2,7 @@ name: pull request ci on: [pull_request] jobs: build: - runs-on: macos-latest + runs-on: macos-14 steps: - name: Checkout last commit uses: actions/checkout@v3 diff --git a/.github/workflows/release-ci.yml b/.github/workflows/release-ci.yml index f17b228bd..cefce9e49 100644 --- a/.github/workflows/release-ci.yml +++ b/.github/workflows/release-ci.yml @@ -5,7 +5,7 @@ on: - '*' jobs: build: - runs-on: macos-latest + runs-on: macos-14 env: SQUIRREL_BUNDLED_RECIPES: 'lotem/rime-octagram-data lotem/rime-octagram-data@hant' steps: diff --git a/INSTALL.md b/INSTALL.md index fecad9710..0e8f78d3e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -61,7 +61,7 @@ export BUILD_UNIVERSAL=1 bash librime/install-boost.sh -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost-1.84.0" ``` Let's set `BUILD_UNIVERSAL` to tell `make` that we are building Boost as diff --git a/Info.plist b/Info.plist index 5a1808ca4..ad3a697e0 100644 --- a/Info.plist +++ b/Info.plist @@ -16,24 +16,26 @@ ${PRODUCT_NAME} CFBundlePackageType APPL + CFBundleShortVersionString + ${MARKETING_VERSION} CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) - CFBundleShortVersionString - ${MARKETING_VERSION} ComponentInputModeDict tsInputModeListKey - im.rime.inputmethod.Squirrel.Hant + im.rime.inputmethod.Squirrel.Hans - TISInputSourceID - ${PRODUCT_BUNDLE_IDENTIFIER}.Hant - TISIntendedLanguage - zh-Hant TISIconIsTemplate + TISInputSourceID + im.rime.inputmethod.Squirrel.Hans + TISIntendedLanguage + zh-Hans + tsInputModeAlternateMenuIconFileKey + rime tsInputModeCharacterRepertoireKey Hani @@ -46,21 +48,21 @@ 4608 tsInputModeMenuIconFileKey rime - tsInputModeAlternateMenuIconFileKey - rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript - im.rime.inputmethod.Squirrel.Hans + im.rime.inputmethod.Squirrel.Hant - TISInputSourceID - ${PRODUCT_BUNDLE_IDENTIFIER}.Hans - TISIntendedLanguage - zh-Hans TISIconIsTemplate + TISInputSourceID + im.rime.inputmethod.Squirrel.Hant + TISIntendedLanguage + zh-Hant + tsInputModeAlternateMenuIconFileKey + rime tsInputModeCharacterRepertoireKey Hani @@ -73,21 +75,21 @@ 4608 tsInputModeMenuIconFileKey rime - tsInputModeAlternateMenuIconFileKey - rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript im.rime.inputmethod.Squirrel.Cant + TISIconIsTemplate + TISInputSourceID - ${PRODUCT_BUNDLE_IDENTIFIER}.Cant + im.rime.inputmethod.Squirrel.Cant TISIntendedLanguage yue-Hant - TISIconIsTemplate - + tsInputModeAlternateMenuIconFileKey + rime tsInputModeCharacterRepertoireKey Hani @@ -100,10 +102,8 @@ 4608 tsInputModeMenuIconFileKey rime - tsInputModeAlternateMenuIconFileKey - rime tsInputModePrimaryInScriptKey - + tsInputModeScriptKey smUnicodeScript @@ -131,14 +131,14 @@ MainMenu NSPrincipalClass NSApplication + NSSupportsSuddenTermination + SUEnableAutomaticChecks SUFeedURL https://rime.github.io/release/squirrel/appcast.xml SUPublicDSAKeyFile dsa_pub.pem - NSSupportsSuddenTermination - TICapsLockLanguageSwitchCapable TISIconIsTemplate diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj index 62c6e6f9c..7a928db4b 100644 --- a/Squirrel.xcodeproj/project.pbxproj +++ b/Squirrel.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ F478A35B2B10795800D7794A /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97324FDCFA39411CA2CEA /* AppKit.framework */; }; F478A35C2B10795B00D7794A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; }; F48CFB692B326F4900DB9CF9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 447765C725C30E6B002415AF /* Sparkle.framework */; }; + F493BF7B2B76F28A008BD7D0 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F493BF7A2B76F27E008BD7D0 /* UserNotifications.framework */; }; F4E457B92AD97412003F6D79 /* EmojiCategoryTW.ocd2 in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */; }; F4E457BA2AD97412003F6D79 /* t2emoji.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457A92AD97411003F6D79 /* t2emoji.json */; }; F4E457BB2AD97412003F6D79 /* hk2t.json in Copy opencc Files */ = {isa = PBXBuildFile; fileRef = F4E457AA2AD97411003F6D79 /* hk2t.json */; }; @@ -216,8 +217,8 @@ /* Begin PBXFileReference section */ 089C165DFE840E0CC02AAC07 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; - 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 32CA4F630368D1EE00C91783 /* Squirrel_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Squirrel_Prefix.pch; sourceTree = ""; }; 441E636322B7E90C006DCCDD /* cangjie5.schema.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = cangjie5.schema.yaml; path = data/plum/cangjie5.schema.yaml; sourceTree = ""; }; 441E636422B7E90C006DCCDD /* terra_pinyin.dict.yaml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = terra_pinyin.dict.yaml; path = data/plum/terra_pinyin.dict.yaml; sourceTree = ""; }; @@ -294,14 +295,15 @@ 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; A44571AB0DBF42C200F793F9 /* macos_keycode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macos_keycode.h; sourceTree = ""; }; A47C48DE105E8CE8006D528B /* macos_keycode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = macos_keycode.m; sourceTree = ""; }; - A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + A4B8E1B20F645B870094E08B /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; A4FC48CA0F6530EF0069BE81 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = /System/Library/Frameworks/InputMethodKit.framework; sourceTree = ""; }; + E93074B60A5C264700470842 /* InputMethodKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = InputMethodKit.framework; path = System/Library/Frameworks/InputMethodKit.framework; sourceTree = SDKROOT; }; F402BAF72B1D8F40001A4BD6 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/MainMenu.strings"; sourceTree = ""; }; F402BAF82B1D8F47001A4BD6 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/MainMenu.strings"; sourceTree = ""; }; F402BAF92B1D8F57001A4BD6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/MainMenu.strings"; sourceTree = ""; }; - F478A3572B1078D200D7794A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = ""; }; - F478A3582B1078D600D7794A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + F478A3572B1078D200D7794A /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; + F478A3582B1078D600D7794A /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + F493BF7A2B76F27E008BD7D0 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; F4A90994297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = ""; }; F4A90995297F63AE00D9F520 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/InfoPlist.strings"; sourceTree = ""; }; F4E457A82AD97411003F6D79 /* EmojiCategoryTW.ocd2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = EmojiCategoryTW.ocd2; sourceTree = ""; }; @@ -337,6 +339,7 @@ F4EC47AA2B3239A2004862A4 /* IOKit.framework in Frameworks */, F478A35A2B1078F900D7794A /* QuartzCore.framework in Frameworks */, F48CFB692B326F4900DB9CF9 /* Sparkle.framework in Frameworks */, + F493BF7B2B76F28A008BD7D0 /* UserNotifications.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -382,6 +385,7 @@ E93074B60A5C264700470842 /* InputMethodKit.framework */, F4EC47A92B323999004862A4 /* IOKit.framework */, F478A3572B1078D200D7794A /* QuartzCore.framework */, + F493BF7A2B76F27E008BD7D0 /* UserNotifications.framework */, ); name = "Other Frameworks"; sourceTree = ""; @@ -670,8 +674,13 @@ ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; @@ -727,8 +736,13 @@ ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_FRAMEWORKS = AppKit; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_STYLE = Automatic; @@ -785,6 +799,14 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_ARC_EXCEPTIONS = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; + CLANG_WARN_ASSIGN_ENUM = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; @@ -792,6 +814,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_FLOAT_CONVERSION = YES; + CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES; + CLANG_WARN_IMPLICIT_FALLTHROUGH = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; @@ -800,6 +826,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -807,12 +834,18 @@ DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)", @@ -846,6 +879,14 @@ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; + CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_ARC_EXCEPTIONS = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; + CLANG_WARN_ASSIGN_ENUM = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; @@ -853,6 +894,10 @@ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_FLOAT_CONVERSION = YES; + CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES; + CLANG_WARN_IMPLICIT_FALLTHROUGH = YES; + CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; @@ -861,6 +906,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -868,12 +914,18 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/Release"; DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_INPUT_FILETYPE = sourcecode.cpp.objcpp; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; + GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(HEADER_SEARCH_PATHS_QUOTED_FOR_PROJECT_1)", diff --git a/autobuild.sh b/autobuild.sh index 05ee42ab5..4e4135dcb 100644 --- a/autobuild.sh +++ b/autobuild.sh @@ -7,11 +7,11 @@ bash librime/install-plugins.sh lotem/librime-octagram hchunhui/librime-lua rime export CMAKE_GENERATOR=Ninja export BUILD_UNIVERSAL=1 bash librime/install-boost.sh -export BOOST_ROOT="$(pwd)/librime/deps/boost_1_83_0" +export BOOST_ROOT="$(pwd)/librime/deps/boost-1.84.0" make -C librime deps # export PATH="/opt/homebrew/opt/llvm/bin:/usr/local/opt/llvm/bin:$PATH" -make -C librime +make -C librime merged-plugins make deps make install diff --git a/data/squirrel.yaml b/data/squirrel.yaml index 7b179b940..89c721014 100644 --- a/data/squirrel.yaml +++ b/data/squirrel.yaml @@ -377,7 +377,9 @@ app_options: org.alacritty: ascii_mode: true vim_mode: true + inline_placeholder: true panelless_commit_fix: true + inline_offset: -10 com.google.Chrome: # 規避 https://github.com/rime/squirrel/issues/435 inline: true From 610af392146f23d37e361fba619cffbdd4c22077 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Fri, 22 Dec 2023 03:16:41 +0100 Subject: [PATCH 157/164] background image & fix various bugs --- SquirrelApplicationDelegate.h | 4 +- SquirrelApplicationDelegate.m | 130 +- SquirrelConfig.h | 11 +- SquirrelConfig.m | 108 +- SquirrelInputController.h | 5 +- SquirrelInputController.m | 242 ++-- SquirrelPanel.h | 8 +- SquirrelPanel.m | 2459 +++++++++++++++++++-------------- input_source.m | 52 +- macos_keycode.m | 208 ++- main.m | 34 +- 11 files changed, 1960 insertions(+), 1301 deletions(-) diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index 153a4ba80..b3c8acb6d 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -5,7 +5,7 @@ @class SquirrelOptionSwitcher; // Note: the SquirrelApplicationDelegate is instantiated automatically as an outlet of NSApp's instance -@interface SquirrelApplicationDelegate : NSObject +@interface SquirrelApplicationDelegate : NSObject @property(nonatomic, copy) IBOutlet NSMenu *menu; @property(nonatomic, strong) IBOutlet SquirrelPanel *panel; @@ -21,7 +21,7 @@ - (IBAction)openLogFolder:(id)sender; - (void)setupRime; -- (void)startRimeWithFullCheck:(BOOL)fullCheck; +- (void)startRimeWithFullCheck:(bool)fullCheck; - (void)loadSettings; - (void)loadSchemaSpecificSettings:(NSString *)schemaId; - (void)loadSchemaSpecificLabels:(NSString *)schemaId; diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 243c14c32..99a41a1ef 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -8,48 +8,44 @@ @implementation SquirrelApplicationDelegate -- (IBAction)deploy:(id)sender -{ +- (IBAction)deploy:(id)sender { NSLog(@"Start maintenance..."); [self shutdownRime]; - [self startRimeWithFullCheck:YES]; + [self startRimeWithFullCheck:true]; [self loadSettings]; } -- (IBAction)syncUserData:(id)sender -{ +- (IBAction)syncUserData:(id)sender { NSLog(@"Sync user data"); rime_get_api()->sync_user_data(); } -- (IBAction)configure:(id)sender -{ - [[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:[@"~/Library/Rime/" stringByExpandingTildeInPath]]]; +- (IBAction)configure:(id)sender { + [NSWorkspace.sharedWorkspace openURL: + [NSURL fileURLWithPath:@"~/Library/Rime/".stringByExpandingTildeInPath isDirectory:YES]]; } -- (IBAction)openWiki:(id)sender -{ - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:kRimeWikiURL]]; +- (IBAction)openWiki:(id)sender { + [NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:kRimeWikiURL]]; } -- (IBAction)openLogFolder:(id)sender -{ - NSString *tmpFolder = [[[NSProcessInfo processInfo] environment] objectForKey:@"TMPDIR"]; - [[NSWorkspace sharedWorkspace] selectFile:[tmpFolder stringByAppendingString:@"rime.squirrel.INFO"] inFileViewerRootedAtPath:tmpFolder]; +- (IBAction)openLogFolder:(id)sender { + NSString *tmpDir = NSTemporaryDirectory(); + NSString *logFile = [tmpDir stringByAppendingPathComponent:@"rime.squirrel.INFO"]; + [NSWorkspace.sharedWorkspace selectFile:logFile + inFileViewerRootedAtPath:tmpDir]; } void show_message(const char *msg_text, const char *msg_id) { @autoreleasepool { - id notification = [[NSClassFromString(@"NSUserNotification") alloc] init]; - [notification performSelector:@selector(setTitle:) - withObject:NSLocalizedString(@"Squirrel", nil)]; - [notification performSelector:@selector(setSubtitle:) - withObject:NSLocalizedString(@(msg_text), nil)]; - id notificationCenter = [(id)NSClassFromString(@"NSUserNotificationCenter") - performSelector:@selector(defaultUserNotificationCenter)]; - [notificationCenter performSelector:@selector(removeAllDeliveredNotifications)]; - [notificationCenter performSelector:@selector(deliverNotification:) - withObject:notification]; + NSUserNotification *notification = [[NSUserNotification alloc] init]; + [notification setTitle:NSLocalizedString(@"Squirrel", nil)]; + [notification setTitle:NSLocalizedString(@(msg_text), nil)]; + + NSUserNotificationCenter *notificationCenter = + NSUserNotificationCenter.defaultUserNotificationCenter; + [notificationCenter removeAllDeliveredNotifications]; + [notificationCenter deliverNotification:notification]; } } @@ -63,8 +59,8 @@ static void show_status_message(const char *msg_text_long, const char *msg_text_ } } -void notification_handler(void *context_object, RimeSessionId session_id, - const char *message_type, const char *message_value) { +static void notification_handler(void *context_object, RimeSessionId session_id, + const char *message_type, const char *message_value) { if (!strcmp(message_type, "deploy")) { if (!strcmp(message_value, "start")) { show_message("deploy_start", message_type); @@ -91,14 +87,18 @@ void notification_handler(void *context_object, RimeSessionId session_id, Bool state = message_value[0] != '!'; const char *option_name = message_value + !state; if ([[app_delegate panel].optionSwitcher containsOption:@(option_name)]) { - if ([[app_delegate panel].optionSwitcher updateGroupState:@(message_value) ofOption:@(option_name)]) { - [app_delegate loadSchemaSpecificSettings:[app_delegate panel].optionSwitcher.schemaId]; - [app_delegate loadSchemaSpecificLabels:[app_delegate panel].optionSwitcher.schemaId]; + if ([[app_delegate panel].optionSwitcher updateGroupState:@(message_value) + ofOption:@(option_name)]) { + NSString *schemaId = [app_delegate panel].optionSwitcher.schemaId; + [app_delegate loadSchemaSpecificLabels:schemaId]; + [app_delegate loadSchemaSpecificSettings:schemaId]; } } if ([app_delegate enableNotifications]) { - RimeStringSlice state_label_long = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, False); - RimeStringSlice state_label_short = rime_get_api()->get_state_label_abbreviated(session_id, option_name, state, True); + RimeStringSlice state_label_long = rime_get_api()-> + get_state_label_abbreviated(session_id, option_name, state, False); + RimeStringSlice state_label_short = rime_get_api()-> + get_state_label_abbreviated(session_id, option_name, state, True); if (state_label_long.str || state_label_short.str) { const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; show_status_message(state_label_long.str, short_message, message_type); @@ -107,36 +107,35 @@ void notification_handler(void *context_object, RimeSessionId session_id, } } -- (void)setupRime -{ - NSString *userDataDir = (@"~/Library/Rime").stringByStandardizingPath; - NSFileManager *fileManager = [NSFileManager defaultManager]; +- (void)setupRime { + NSString *userDataDir = @"~/Library/Rime".stringByExpandingTildeInPath; + NSFileManager *fileManager = [[NSFileManager alloc] init]; if (![fileManager fileExistsAtPath:userDataDir]) { if (![fileManager createDirectoryAtPath:userDataDir withIntermediateDirectories:YES attributes:nil - error:NULL]) { + error:nil]) { NSLog(@"Error creating user data directory: %@", userDataDir); } } rime_get_api()->set_notification_handler(notification_handler, (__bridge void *)(self)); RIME_STRUCT(RimeTraits, squirrel_traits); - squirrel_traits.shared_data_dir = [NSBundle mainBundle].sharedSupportPath.UTF8String; + squirrel_traits.shared_data_dir = NSBundle.mainBundle.sharedSupportPath.UTF8String; squirrel_traits.user_data_dir = userDataDir.UTF8String; squirrel_traits.distribution_code_name = "Squirrel"; squirrel_traits.distribution_name = "鼠鬚管"; squirrel_traits.distribution_version = - [[NSBundle mainBundle].infoDictionary[@"CFBundleVersion"] UTF8String]; + [NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"] UTF8String]; squirrel_traits.app_name = "rime.squirrel"; rime_get_api()->setup(&squirrel_traits); } -- (void)startRimeWithFullCheck:(BOOL)fullCheck +- (void)startRimeWithFullCheck:(bool)fullCheck { NSLog(@"Initializing la rime..."); rime_get_api()->initialize(NULL); // check for configuration updates - if (rime_get_api()->start_maintenance((Bool)fullCheck)) { + if (rime_get_api()->start_maintenance(fullCheck)) { // update squirrel config rime_get_api()->deploy_config_file("squirrel.yaml", "config_version"); } @@ -195,46 +194,45 @@ - (void)loadSchemaSpecificLabels:(NSString *)schemaId { } // prevent freezing the system -- (BOOL)problematicLaunchDetected -{ +- (BOOL)problematicLaunchDetected { BOOL detected = NO; - NSString *logfile = [NSTemporaryDirectory() stringByAppendingPathComponent:@"squirrel_launch.dat"]; - //NSLog(@"[DEBUG] archive: %@", logfile); - NSData *archive = [NSData dataWithContentsOfFile:logfile - options:NSDataReadingUncached - error:nil]; + NSURL *logfile = [[NSURL fileURLWithPath:NSTemporaryDirectory() + isDirectory:YES] URLByAppendingPathComponent:@"squirrel_launch.dat"]; + NSLog(@"[DEBUG] archive: %@", logfile); + NSData *archive = [NSData dataWithContentsOfURL:logfile + options:NSDataReadingUncached + error:nil]; if (archive) { - NSDate *previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:NULL]; + NSDate *previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class + fromData:archive error:nil]; if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } NSDate *now = [NSDate date]; - NSData *record = [NSKeyedArchiver archivedDataWithRootObject:now requiringSecureCoding:NO error:NULL]; - [record writeToFile:logfile atomically:NO]; + NSData *record = [NSKeyedArchiver archivedDataWithRootObject:now + requiringSecureCoding:NO error:nil]; + NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingToURL:logfile error:nil]; + [fileHandle writeData:record]; return detected; } -- (void)workspaceWillPowerOff:(NSNotification *)aNotification -{ +- (void)workspaceWillPowerOff:(NSNotification *)aNotification { NSLog(@"Finalizing before logging out."); [self shutdownRime]; } -- (void)rimeNeedsReload:(NSNotification *)aNotification -{ +- (void)rimeNeedsReload:(NSNotification *)aNotification { NSLog(@"Reloading rime on demand."); [self deploy:nil]; } -- (void)rimeNeedsSync:(NSNotification *)aNotification -{ +- (void)rimeNeedsSync:(NSNotification *)aNotification { NSLog(@"Sync rime on demand."); [self syncUserData:nil]; } -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { NSLog(@"Squirrel is quitting."); rime_get_api()->cleanup_all_sessions(); return NSTerminateNow; @@ -243,15 +241,14 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sende //add an awakeFromNib item so that we can set the action method. Note that //any menuItems without an action will be disabled when displayed in the Text //Input Menu. -- (void)awakeFromNib -{ - NSNotificationCenter *center = [NSWorkspace sharedWorkspace].notificationCenter; +- (void)awakeFromNib { + NSNotificationCenter *center = NSWorkspace.sharedWorkspace.notificationCenter; [center addObserver:self selector:@selector(workspaceWillPowerOff:) name:NSWorkspaceWillPowerOffNotification object:nil]; - NSDistributedNotificationCenter *notifCenter = [NSDistributedNotificationCenter defaultCenter]; + NSDistributedNotificationCenter *notifCenter = NSDistributedNotificationCenter.defaultCenter; [notifCenter addObserver:self selector:@selector(rimeNeedsReload:) name:@"SquirrelReloadNotification" @@ -263,10 +260,9 @@ - (void)awakeFromNib object:nil]; } -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; +- (void)dealloc { + [NSNotificationCenter.defaultCenter removeObserver:self]; + [NSDistributedNotificationCenter.defaultCenter removeObserver:self]; if (_panel) { [_panel hide]; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 69b77d98b..307f4a827 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -24,7 +24,8 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (NSMutableDictionary *)mutableSwitcher; -@end +@end // SquirrelOptionSwitcher + @interface SquirrelConfig : NSObject @@ -44,19 +45,23 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (BOOL)getBool:(NSString *)option; - (int)getInt:(NSString *)option; - (double)getDouble:(NSString *)option; +- (double)getDouble:(NSString *)option + applyConstraint:(double(*)(double param))func; - (NSNumber *)getOptionalBool:(NSString *)option; - (NSNumber *)getOptionalInt:(NSString *)option; - (NSNumber *)getOptionalDouble:(NSString *)option; +- (NSNumber *)getOptionalDouble:(NSString *)option + applyConstraint:(double(*)(double param))func; - (NSString *)getString:(NSString *)option; // 0xaabbggrr or 0xbbggrr - (NSColor *)getColor:(NSString *)option; // file path (absolute or relative to ~/Library/Rime) -- (NSColor *)getPattern:(NSString *)option; +- (NSImage *)getImage:(NSString *)option; - (NSArray *)getList:(NSString *)option; - (SquirrelOptionSwitcher *)getOptionSwitcher; - (SquirrelAppOptions *)getAppOptions:(NSString *)appName; -@end +@end // SquirrelConfig diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 13a850840..fc1730076 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -11,13 +11,13 @@ @implementation SquirrelOptionSwitcher { - (instancetype)initWithSchemaId:(NSString *)schemaId switcher:(NSDictionary *)switcher - optionGroups:(NSDictionary *> *)optionGroups{ + optionGroups:(NSDictionary *> *)optionGroups { self = [super init]; if (self) { _schemaId = schemaId; _switcher = switcher; _optionGroups = optionGroups; - _optionNames = [switcher allKeys]; + _optionNames = switcher.allKeys; } return self; } @@ -31,7 +31,7 @@ - (NSString *)schemaId { } - (NSArray *)optionStates { - return [_switcher allValues]; + return _switcher.allValues; } - (NSDictionary *)switcher { @@ -54,7 +54,8 @@ - (BOOL)updateSwitcher:(NSDictionary *)switcher { return YES; } -- (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName { +- (BOOL)updateGroupState:(NSString *)optionState + ofOption:(NSString *)optionName { NSArray *optionGroup = _optionGroups[optionName]; if (!optionGroup || ![optionGroup containsObject:optionState]) { return NO; @@ -78,7 +79,7 @@ - (BOOL)containsOption:(NSString *)optionName { @end @implementation SquirrelConfig { - NSMutableDictionary *_cache; + NSCache *_cache; RimeConfig _config; NSString *_schemaId; SquirrelConfig *_baseConfig; @@ -88,7 +89,7 @@ @implementation SquirrelConfig { - (instancetype)init { self = [super init]; if (self) { - _cache = [[NSMutableDictionary alloc] init]; + _cache = [[NSCache alloc] init]; } self.colorSpace = @"srgb"; return self; @@ -162,79 +163,96 @@ - (double)getDouble:(NSString *)option { return [self getOptionalDouble:option].doubleValue; } +- (double)getDouble:(NSString *)option + applyConstraint:(double(*)(double param))func { + NSNumber *value = [self getOptionalDouble:option]; + return func(value.doubleValue); +} + - (NSNumber *)getOptionalBool:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:[NSNumber class] forKey:option]; + NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; if (cachedValue) { return cachedValue; } Bool value; if (_isOpen && rime_get_api()->config_get_bool(&_config, option.UTF8String, &value)) { - return _cache[option] = @(!!value); + [_cache setObject:@(!!value) forKey:option]; + return @(!!value); } return [_baseConfig getOptionalBool:option]; } - (NSNumber *)getOptionalInt:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:[NSNumber class] forKey:option]; + NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; if (cachedValue) { return cachedValue; } int value; if (_isOpen && rime_get_api()->config_get_int(&_config, option.UTF8String, &value)) { - return _cache[option] = @(value); + [_cache setObject:@(value) forKey:option]; + return @(value); } return [_baseConfig getOptionalInt:option]; } - (NSNumber *)getOptionalDouble:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:[NSNumber class] forKey:option]; + NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; if (cachedValue) { return cachedValue; } double value; if (_isOpen && rime_get_api()->config_get_double(&_config, option.UTF8String, &value)) { - return _cache[option] = @(value); + [_cache setObject:@(value) forKey:option]; + return @(value); } return [_baseConfig getOptionalDouble:option]; } +- (NSNumber *)getOptionalDouble:(NSString *)option + applyConstraint:(double(*)(double param))func { + NSNumber *value = [self getOptionalDouble:option]; + return value ? @(func(value.doubleValue)) : nil; +} + - (NSString *)getString:(NSString *)option { - NSString *cachedValue = [self cachedValueOfClass:[NSString class] forKey:option]; + NSString *cachedValue = [self cachedValueOfClass:NSString.class forKey:option]; if (cachedValue) { return cachedValue; } const char *value = _isOpen ? rime_get_api()->config_get_cstring(&_config, option.UTF8String) : NULL; if (value) { - return _cache[option] = @(value); + NSString *string = [@(value) stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; + [_cache setObject:string forKey:option]; + return string; } return [_baseConfig getString:option]; } - (NSColor *)getColor:(NSString *)option { - NSColor *cachedValue = [self cachedValueOfClass:[NSColor class] forKey:option]; + NSColor *cachedValue = [self cachedValueOfClass:NSColor.class forKey:option]; if (cachedValue) { return cachedValue; } NSColor *color = [self colorFromString:[self getString:option]]; if (color) { - _cache[option] = color; + [_cache setObject:color forKey:option]; return color; } return [_baseConfig getColor:option]; } -- (NSColor *)getPattern:(NSString *)option { - NSColor *cachedValue = [self cachedValueOfClass:[NSColor class] forKey:option]; +- (NSImage *)getImage:(NSString *)option { + NSImage *cachedValue = [self cachedValueOfClass:NSImage.class forKey:option]; if (cachedValue) { return cachedValue; } - NSColor *pattern = [self patternFromFile:[self getString:option]]; - if (pattern) { - _cache[option] = pattern; - return pattern; + NSImage *image = [self imageFromFile:[self getString:option]]; + if (image) { + [_cache setObject:image forKey:option]; + return image; } - return [_baseConfig getPattern:option]; + return [_baseConfig getImage:option]; } - (NSArray *)getList:(NSString *)option { @@ -253,7 +271,7 @@ - (NSColor *)getPattern:(NSString *)option { - (SquirrelOptionSwitcher *)getOptionSwitcher { RimeConfigIterator switchIter; if (!rime_get_api()->config_begin_list(&switchIter, &_config, "switches")) { - return nil; + return nil; } NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; NSMutableDictionary *> *optionGroups = [[NSMutableDictionary alloc] init]; @@ -304,14 +322,15 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { appOptions[@(iterator.key)] = @(value); } rime_get_api()->config_end(&iterator); - return [appOptions copy]; + return appOptions; } #pragma mark - Private methods -- (id)cachedValueOfClass:(Class)aClass forKey:(NSString *)key { +- (id)cachedValueOfClass:(Class)aClass + forKey:(NSString *)key { id value = [_cache objectForKey:key]; - if (value && [value isKindOfClass:aClass]) { + if (value && [value isMemberOfClass:aClass]) { return value; } return nil; @@ -324,35 +343,36 @@ - (NSColor *)colorFromString:(NSString *)string { int r = 0, g = 0, b = 0, a = 0xff; if (string.length == 10) { - // 0xffccbbaa + // 0xaaBBGGRR sscanf(string.UTF8String, "0x%02x%02x%02x%02x", &a, &b, &g, &r); } else if (string.length == 8) { - // 0xccbbaa + // 0xBBGGRR sscanf(string.UTF8String, "0x%02x%02x%02x", &b, &g, &r); } if ([self.colorSpace isEqualToString:@"display_p3"]) { - return [NSColor colorWithDisplayP3Red:(CGFloat)r / 255. - green:(CGFloat)g / 255. - blue:(CGFloat)b / 255. - alpha:(CGFloat)a / 255.]; + return [NSColor colorWithDisplayP3Red:r / 255.0 + green:g / 255.0 + blue:b / 255.0 + alpha:a / 255.0]; } else { // sRGB by default - return [NSColor colorWithSRGBRed:(CGFloat)r / 255. - green:(CGFloat)g / 255. - blue:(CGFloat)b / 255. - alpha:(CGFloat)a / 255.]; + return [NSColor colorWithSRGBRed:r / 255.0 + green:g / 255.0 + blue:b / 255.0 + alpha:a / 255.0]; } } -- (NSColor *)patternFromFile:(NSString *)filePath { +- (NSImage *)imageFromFile:(NSString *)filePath { if (filePath == nil) { return nil; } - NSFileManager *fileManager = [NSFileManager defaultManager]; - [fileManager changeCurrentDirectoryPath:[@"~/Library/Rime" stringByStandardizingPath]]; - NSString *patternFile = [filePath stringByStandardizingPath]; - if ([fileManager fileExistsAtPath:patternFile]) { - NSColor *pattern = [NSColor colorWithPatternImage:[[NSImage alloc] initByReferencingFile:patternFile]]; - return pattern; + NSURL *userDataDir = [NSURL fileURLWithPath:@"~/Library/Rime".stringByExpandingTildeInPath + isDirectory:YES]; + NSURL *imageFile = [NSURL fileURLWithPath:filePath + isDirectory:NO relativeToURL:userDataDir]; + if ([imageFile checkResourceIsReachableAndReturnError:nil]) { + NSImage *image = [[NSImage alloc] initByReferencingURL:imageFile]; + return image; } return nil; } diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 54f007db9..ee11f5621 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -9,13 +9,16 @@ typedef enum { kDELETE = 3 // only accepts indices in digits (int 1) } rimeAction; -typedef enum { +typedef enum rimeIndex : NSUInteger { // 0 ... 9 are ordinal digits, used as (int) index // 0x21 ... 0x7e are ASCII chars (as selection keys) // other rime keycodes (as function keys), for paging etc. + kBackSpace = 0xff08, // XK_BackSpace kEscape = 0xff1b, // XK_Escape + kHome = 0xff50, // XK_Home kPageUp = 0xff55, // XK_Page_Up kPageDown = 0xff56, // XK_Page_Down + kEnd = 0xff57, // XK_End kVoidSymbol = 0xffffff // XK_VoidSymbol } rimeIndex; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 6c6501cce..2fcdbebdb 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -6,7 +6,6 @@ #import "macos_keycode.h" #import #import -#import #import #import @@ -52,13 +51,11 @@ @implementation SquirrelInputController { @discussion This method receives key events from the client application. */ - (BOOL)handleEvent:(NSEvent *)event - client:(id)sender -{ + client:(id)sender { // Return YES to indicate the the key input was received and dealt with. // Key processing will not continue in that case. In other words the // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @autoreleasepool { @@ -68,6 +65,8 @@ - (BOOL)handleEvent:(NSEvent *)event return NO; } } + NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; + int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); switch (event.type) { case NSEventTypeFlagsChanged: { @@ -78,7 +77,6 @@ - (BOOL)handleEvent:(NSEvent *)event //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); ushort keyCode = (ushort)CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); uint eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + @@ -90,7 +88,7 @@ - (BOOL)handleEvent:(NSEvent *)event switch (keyCode) { case kVK_CapsLock: if (!_goodOldCapsLock) { - set_caps_lock_led(false); + set_CapsLock_LED_state(false); Bool caps_lock_on = rime_get_api()->get_option(_session, "caps_lock_on"); rime_modifiers = caps_lock_on ? rime_modifiers | kLockMask : rime_modifiers & ~kLockMask; rime_get_api()->set_option(_session, "caps_lock_on", !caps_lock_on); @@ -101,26 +99,31 @@ - (BOOL)handleEvent:(NSEvent *)event break; case kVK_Shift: case kVK_RightShift: - release_mask = modifiers & NSEventModifierFlagShift ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & NSEventModifierFlagShift ? 0 : + kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Control: case kVK_RightControl: - release_mask = modifiers & NSEventModifierFlagControl ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & NSEventModifierFlagControl ? 0 : + kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Option: case kVK_RightOption: - release_mask = modifiers & NSEventModifierFlagOption ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & NSEventModifierFlagOption ? 0 : + kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Function: - release_mask = modifiers & NSEventModifierFlagFunction ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & NSEventModifierFlagFunction ? 0 : + kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Command: case kVK_RightCommand: - release_mask = modifiers & NSEventModifierFlagCommand ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + release_mask = modifiers & NSEventModifierFlagCommand ? 0 : + kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; } @@ -139,7 +142,6 @@ - (BOOL)handleEvent:(NSEvent *)event modifiers & NSEventModifierFlagShift, modifiers & NSEventModifierFlagCapsLock); if (rime_keycode) { - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) if ((keyCode <= 0xff && keyCode >= 0x60) || keyCode == 0x50 || keyCode == 0x4f || keyCode == 0x47 || keyCode == 0x40) { @@ -156,7 +158,7 @@ - (BOOL)handleEvent:(NSEvent *)event return handled; } -void set_caps_lock_led(bool target_state) { +void set_CapsLock_LED_state(bool target_state) { io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass)); io_connect_t ioConnect = 0; IOServiceOpen(ioService, mach_task_self_, kIOHIDParamConnectType, &ioConnect); @@ -169,8 +171,7 @@ void set_caps_lock_led(bool target_state) { } - (BOOL)processKey:(int)rime_keycode - modifiers:(int)rime_modifiers -{ + modifiers:(int)rime_modifiers { // with linear candidate list, arrow keys may behave differently. Bool is_linear = NSApp.squirrelAppDelegate.panel.linear; if (is_linear != rime_get_api()->get_option(_session, "_linear")) { @@ -216,12 +217,13 @@ - (BOOL)processKey:(int)rime_keycode return handled; } -- (void)perform:(rimeAction)action +- (void)perform:(rimeAction)action onIndex:(rimeIndex)index { + //NSLog(@"perform action: %u on index: %lu", action, (unsigned long)index); bool handled = false; if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) { handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0); - } else if ((index == kPageUp || index == kPageDown || index == kEscape) && action == kSELECT) { + } else if (index >= 0xff08 && index <= 0xffff && action == kSELECT) { handled = rime_get_api()->process_key(_session, (int)index, 0); } else if (index >= 0 && index < 10) { switch (action) { @@ -241,10 +243,9 @@ - (void)perform:(rimeAction)action } } -- (void)onChordTimer:(NSTimer *)timer -{ +- (void)onChordTimer:(NSTimer *)timer { // chord release triggered by timer - int processed_keys = 0; + uint processed_keys = 0; if (_chordKeyCount && _session) { // simulate key-ups for (uint i = 0; i < _chordKeyCount; ++i) { @@ -255,14 +256,13 @@ - (void)onChordTimer:(NSTimer *)timer } } [self clearChord]; - if (processed_keys) { + if (processed_keys > 0) { [self rimeUpdate]; } } - (void)updateChord:(int)keycode - modifiers:(int)modifiers -{ + modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); for (uint i = 0; i < _chordKeyCount; ++i) { if (_chordKeyCodes[i] == keycode) { @@ -281,7 +281,8 @@ - (void)updateChord:(int)keycode [_chordTimer invalidate]; } _chordDuration = 0.1; - NSNumber *duration = [NSApp.squirrelAppDelegate.config getOptionalDouble:@"chord_duration"]; + NSNumber *duration = [NSApp.squirrelAppDelegate.config + getOptionalDouble:@"chord_duration"]; if (duration && duration.doubleValue > 0) { _chordDuration = duration.doubleValue; } @@ -292,8 +293,7 @@ - (void)updateChord:(int)keycode repeats:NO]; } -- (void)clearChord -{ +- (void)clearChord { _chordKeyCount = 0; if (_chordTimer) { if (_chordTimer.valid) { @@ -303,42 +303,45 @@ - (void)clearChord } } -- (NSUInteger)recognizedEvents:(id)sender -{ +- (NSUInteger)recognizedEvents:(id)sender { //NSLog(@"recognizedEvents:"); return NSEventMaskKeyDown | NSEventMaskFlagsChanged; } -- (void)activateServer:(id)sender -{ +- (void)activateServer:(id)sender { //NSLog(@"activateServer:"); - NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; - if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { + NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config + getString:@"keyboard_layout"]; + if ([keyboardLayout isEqualToString:@"last"] || + [keyboardLayout isEqualToString:@""]) { keyboardLayout = nil; } else if ([keyboardLayout isEqualToString:@"default"]) { keyboardLayout = @"com.apple.keylayout.ABC"; } else if (![keyboardLayout hasPrefix:@"com.apple.keylayout."]) { - keyboardLayout = [@"com.apple.keylayout." stringByAppendingString:keyboardLayout]; + keyboardLayout = [@"com.apple.keylayout." + stringByAppendingString:keyboardLayout]; } if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; - if ([defaultConfig openWithConfigId:@"default"] && [defaultConfig hasSection:@"ascii_composer"]) { - _goodOldCapsLock = [defaultConfig getBool:@"ascii_composer/good_old_caps_lock"]; + if ([defaultConfig openWithConfigId:@"default"] && + [defaultConfig hasSection:@"ascii_composer"]) { + _goodOldCapsLock = [defaultConfig getBool: + @"ascii_composer/good_old_caps_lock"]; } [defaultConfig close]; [super activateServer:sender]; } -- (instancetype)initWithServer:(IMKServer *)server +- (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate - client:(id)inputClient -{ + client:(id)inputClient { //NSLog(@"initWithServer:delegate:client:"); - if (self = [super initWithServer:server delegate:delegate client:inputClient]) { + if (self = [super initWithServer:server delegate:delegate + client:inputClient]) { [self createSession]; _preeditString = [[NSMutableAttributedString alloc] init]; _originalString = [[NSMutableString alloc] init]; @@ -347,8 +350,7 @@ - (instancetype)initWithServer:(IMKServer *)server return self; } -- (void)deactivateServer:(id)sender -{ +- (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); [self commitComposition:sender]; [super deactivateServer:sender]; @@ -365,8 +367,7 @@ - (void)deactivateServer:(id)sender to clean up if that is necessary. */ -- (void)commitComposition:(id)sender -{ +- (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); if (_session) { [self commitString:[self composedString:sender]]; @@ -375,86 +376,80 @@ - (void)commitComposition:(id)sender } } +- (void)inputControllerWillClose { + if (_session) { + [self destroySession]; + } + _preeditString = nil; + _originalString = nil; + _composedString = nil; +} + // a piece of comment from SunPinyin's macos wrapper says: // > though we specified the showPrefPanel: in SunPinyinApplicationDelegate as the // > action receiver, the IMKInputController will actually receive the event. // so here we deliver messages to our responsible SquirrelApplicationDelegate -- (void)deploy:(id)sender -{ +- (void)deploy:(id)sender { [NSApp.squirrelAppDelegate deploy:sender]; } -- (void)syncUserData:(id)sender -{ +- (void)syncUserData:(id)sender { [NSApp.squirrelAppDelegate syncUserData:sender]; } -- (void)configure:(id)sender -{ +- (void)configure:(id)sender { [NSApp.squirrelAppDelegate configure:sender]; } -- (void)checkForUpdates:(id)sender -{ - [NSApp.squirrelAppDelegate.updater performSelector:@selector(checkForUpdates:) +- (void)checkForUpdates:(id)sender { + [NSApp.squirrelAppDelegate.updater performSelector:@selector(checkForUpdates:) withObject:sender]; } -- (void)openWiki:(id)sender -{ +- (void)openWiki:(id)sender { [NSApp.squirrelAppDelegate openWiki:sender]; } -- (void)openLogFolder:(id)sender -{ +- (void)openLogFolder:(id)sender { [NSApp.squirrelAppDelegate openLogFolder:sender]; } -- (NSMenu *)menu -{ +- (NSMenu *)menu { return NSApp.squirrelAppDelegate.menu; } -- (NSAttributedString *)originalString:(id)sender -{ +- (NSAttributedString *)originalString:(id)sender { return [[NSAttributedString alloc] initWithString:_originalString]; } -- (id)composedString:(id)sender -{ +- (id)composedString:(id)sender { return _composedString; } -- (NSArray *)candidates:(id)sender -{ +- (NSArray *)candidates:(id)sender { return _candidates; } -- (void)hidePalettes -{ +- (void)hidePalettes { [NSApp.squirrelAppDelegate.panel hide]; } -- (void)dealloc -{ +- (void)dealloc { [self destroySession]; _preeditString = nil; _originalString = nil; _composedString = nil; } -- (NSRange)selectionRange -{ +- (NSRange)selectionRange { return NSMakeRange(_caretPos, 0); } -- (NSRange)replacementRange -{ +- (NSRange)replacementRange { return NSMakeRange(NSNotFound, NSNotFound); } -- (void)commitString:(id)string -{ +- (void)commitString:(id)string { //NSLog(@"commitString:"); [self.client insertText:string replacementRange:self.replacementRange]; @@ -464,14 +459,12 @@ - (void)commitString:(id)string [_composedString deleteCharactersInRange:NSMakeRange(0, _composedString.length)]; } -- (void)cancelComposition -{ +- (void)cancelComposition { [self commitString:[self originalString:self.client]]; rime_get_api()->clear_composition(_session); } -- (void)updateComposition -{ +- (void)updateComposition { [self.client setMarkedText:_preeditString selectionRange:self.selectionRange replacementRange:self.replacementRange]; @@ -479,8 +472,7 @@ - (void)updateComposition - (void)showPreeditString:(NSString *)preedit selRange:(NSRange)range - caretPos:(NSUInteger)pos -{ + caretPos:(NSUInteger)pos { //NSLog(@"showPreeditString: '%@'", preedit); if (_inlinePlaceHolder && _candidates.count > 0 && preedit.length == 0) { preedit = @" "; @@ -515,18 +507,29 @@ - (void)showPanelWithPreedit:(NSString *)preedit comments:(NSArray *)comments highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage -{ + lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; - NSRect inputPos; - [self.client attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; + NSRect inputPos; + [self.client attributesForCharacterIndex:0 + lineHeightRectangle:&inputPos]; + if (NSEqualRects(inputPos, NSZeroRect) && _preeditString.length == 0) { + // activate inline session, in e.g. table cells, by fake inputs + [self.client setMarkedText:@" " + selectionRange:NSMakeRange(0, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + [self.client attributesForCharacterIndex:0 + lineHeightRectangle:&inputPos]; + [self.client setMarkedText:_preeditString + selectionRange:NSMakeRange(0, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + } if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view if (_goodOldCapsLock && _lastModifier & NSEventModifierFlagCapsLock) { - NSRect screenRect = [[NSScreen mainScreen] frame]; + NSRect screenRect = NSScreen.mainScreen.frame; if (NSIntersectsRect(inputPos, screenRect)) { - screenRect = [[NSScreen mainScreen] visibleFrame]; + screenRect = NSScreen.mainScreen.visibleFrame; if (NSWidth(inputPos) > NSHeight(inputPos)) { NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos) - 30, NSMinY(inputPos), 27, NSHeight(inputPos)); if (NSMinX(capslockAccessory) < NSMinX(screenRect)) @@ -546,7 +549,6 @@ - (void)showPanelWithPreedit:(NSString *)preedit } } panel.inputController = self; - panel.level = self.client.windowLevel + 1; panel.position = inputPos; [panel showPreedit:preedit selRange:selRange @@ -564,9 +566,8 @@ - (void)showPanelWithPreedit:(NSString *)preedit // implementation of private interface @implementation SquirrelInputController (Private) -- (void)createSession -{ - NSString *app = [self.client bundleIdentifier]; +- (void)createSession { + NSString *app = self.client.bundleIdentifier; //NSLog(@"createSession: %@", app); _session = rime_get_api()->create_session(); _schemaId = nil; @@ -578,12 +579,12 @@ - (void)createSession SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:app]; if (appOptions) { for (NSString *key in appOptions) { - Bool value = [appOptions[key] intValue]; + Bool value = appOptions[key].intValue; //NSLog(@"set app option: %@ = %d", key, value); rime_get_api()->set_option(_session, key.UTF8String, value); } - _panellessCommitFix = [appOptions[@"panelless_commit_fix"] boolValue]; - _inlinePlaceHolder = [appOptions[@"inline_placeholder"] boolValue]; + _panellessCommitFix = appOptions[@"panelless_commit_fix"].boolValue; + _inlinePlaceHolder = appOptions[@"inline_placeholder"].boolValue; } } _lastModifier = 0; @@ -592,8 +593,7 @@ - (void)createSession } } -- (void)destroySession -{ +- (void)destroySession { //NSLog(@"destroySession:"); if (_session) { rime_get_api()->destroy_session(_session); @@ -602,32 +602,35 @@ - (void)destroySession [self clearChord]; } -- (void)rimeConsumeCommittedText -{ +- (void)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); if (_preeditString.length == 0 && _panellessCommitFix) { - [self showPreeditString:@" " selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@" " + selRange:NSMakeRange(0, 0) + caretPos:0]; } [self commitString:commitText]; rime_get_api()->free_commit(&commit); } } -- (void)updateStyleOptions -{ +- (void)updateStyleOptions { // update the list of switchers that change styles and color-themes SquirrelOptionSwitcher *optionSwitcher; SquirrelConfig *schema = [[SquirrelConfig alloc] init]; - if ([schema openWithSchemaId:_schemaId baseConfig:NSApp.squirrelAppDelegate.config] && + if ([schema openWithSchemaId:_schemaId + baseConfig:NSApp.squirrelAppDelegate.config] && [schema hasSection:@"style"]) { optionSwitcher = [schema getOptionSwitcher]; } else { - optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:_schemaId switcher:@{} optionGroups:@{}]; + optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:_schemaId + switcher:@{} + optionGroups:@{}]; } [schema close]; - NSMutableDictionary *switcher = [optionSwitcher mutableSwitcher]; + NSMutableDictionary *switcher = optionSwitcher.mutableSwitcher; NSSet *prevStates = [NSSet setWithArray:optionSwitcher.optionStates]; for (NSString *state in prevStates) { NSString *updatedState; @@ -646,11 +649,10 @@ - (void)updateStyleOptions } } [optionSwitcher updateSwitcher:switcher]; - [NSApp.squirrelAppDelegate.panel setOptionSwitcher:optionSwitcher]; + NSApp.squirrelAppDelegate.panel.optionSwitcher = optionSwitcher; } -- (void)rimeUpdate -{ +- (void)rimeUpdate { //NSLog(@"rimeUpdate"); [self rimeConsumeCommittedText]; @@ -704,10 +706,18 @@ - (void)rimeUpdate [_composedString setString:[@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]]; } - NSUInteger start = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; - NSUInteger end = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; - NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; - NSUInteger length = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.length encoding:NSUTF8StringEncoding].length; + NSUInteger start = [[NSString alloc] initWithBytes:preedit + length:(NSUInteger)ctx.composition.sel_start + encoding:NSUTF8StringEncoding].length; + NSUInteger end = [[NSString alloc] initWithBytes:preedit + length:(NSUInteger)ctx.composition.sel_end + encoding:NSUTF8StringEncoding].length; + NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit + length:(NSUInteger)ctx.composition.cursor_pos + encoding:NSUTF8StringEncoding].length; + NSUInteger length = [[NSString alloc] initWithBytes:preedit + length:(NSUInteger)ctx.composition.length + encoding:NSUTF8StringEncoding].length; NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; // update candidates @@ -717,9 +727,9 @@ - (void)rimeUpdate [candidates addObject:@(ctx.menu.candidates[i].text)]; [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; } - [self showPanelWithPreedit:(_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText) + [self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText selRange:NSMakeRange(start, end - start) - caretPos:(_showingSwitcherMenu ? NSNotFound : caretPos) + caretPos:_showingSwitcherMenu ? NSNotFound : caretPos candidates:candidates comments:comments highlighted:(NSUInteger)ctx.menu.highlighted_candidate_index @@ -752,9 +762,13 @@ - (void)rimeUpdate } } else { if (_inlinePreedit && !_showingSwitcherMenu) { - [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; + [self showPreeditString:preeditText + selRange:NSMakeRange(start, end - start) + caretPos:caretPos]; } else { - [self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0]; + [self showPreeditString:@"" + selRange:NSMakeRange(0, 0) + caretPos:0]; } } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index e0849c533..1533fbf2c 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -6,11 +6,11 @@ typedef enum { defaultAppear = 0, - lightAppear = 0, - darkAppear = 1 + lightAppear = 0, + darkAppear = 1 } SquirrelAppear; -@interface SquirrelPanel : NSPanel +@interface SquirrelPanel : NSPanel // Linear candidate list, as opposed to stacked candidate list. @property(nonatomic, readonly) BOOL linear; @@ -34,7 +34,7 @@ typedef enum { caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - highlighted:(NSUInteger)index + highlighted:(NSUInteger)highlighted pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index e798285f9..b8055d0e1 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -3,7 +3,7 @@ #import "SquirrelConfig.h" #import -static const CGFloat kOffsetHeight = 5; +static const CGFloat kOffsetGap = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; @@ -67,41 +67,62 @@ - (CGPathRef)quartzPath { @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) +- (void)superscriptRange:(NSRange)range { + [self enumerateAttribute:NSFontAttributeName inRange:range + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + NSFont *font = [NSFont fontWithDescriptor:value.fontDescriptor + size:floor(value.pointSize * 0.55)]; + [self addAttributes:@{ NSFontAttributeName : font, + (NSString *)kCTBaselineClassAttributeName : (NSString *)kCTBaselineClassIdeographicHigh, + NSSuperscriptAttributeName : @(1)} + range:range]; + }]; +} + +- (void)subscriptRange:(NSRange)range { + [self enumerateAttribute:NSFontAttributeName inRange:range + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + NSFont *font = [NSFont fontWithDescriptor:value.fontDescriptor + size:floor(value.pointSize * 0.55)]; + [self addAttributes:@{ NSFontAttributeName : font, + (NSString *)kCTBaselineClassAttributeName : (NSString *)kCTBaselineClassIdeographicLow, + NSSuperscriptAttributeName : @(-1)} + range:range]; + }]; +} + - (void)formatMarkDown { NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: @"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; NSInteger __block offset = 0; - [regex enumerateMatchesInString:self.string options:0 range:NSMakeRange(0, self.length) + [regex enumerateMatchesInString:self.string options:0 + range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { result = [result resultByAdjustingRangesWithOffset:offset]; NSString *tag = [self.string substringWithRange:[result rangeAtIndex:1]]; if ([tag isEqualToString:@"**"] || [tag isEqualToString:@"__"] || [tag isEqualToString:@""] || [tag isEqualToString:@""]) { - [self applyFontTraits:NSBoldFontMask range:[result rangeAtIndex:5]]; + [self applyFontTraits:NSBoldFontMask + range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"*"] || [tag isEqualToString:@"_"] || [tag isEqualToString:@""] || [tag isEqualToString:@""]) { - [self applyFontTraits:NSItalicFontMask range:[result rangeAtIndex:5]]; + [self applyFontTraits:NSItalicFontMask + range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@""]) { [self addAttribute:NSUnderlineStyleAttributeName - value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + value:@(NSUnderlineStyleSingle) + range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"~~"] || [tag isEqualToString:@""]) { [self addAttribute:NSStrikethroughStyleAttributeName - value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; + value:@(NSUnderlineStyleSingle) + range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { [self superscriptRange:[result rangeAtIndex:5]]; - [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { - NSFont *font = [NSFont fontWithDescriptor:[value fontDescriptor] size:[value pointSize] * 7 / 12]; - [self addAttribute:NSFontAttributeName value:font range:range]; - }]; } else if ([tag isEqualToString:@"~"] || [tag isEqualToString:@""]) { [self subscriptRange:[result rangeAtIndex:5]]; - [self enumerateAttribute:NSFontAttributeName inRange:[result rangeAtIndex:5] options:0 - usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { - NSFont *font = [NSFont fontWithDescriptor:[value fontDescriptor] size:[value pointSize] * 7 / 12]; - [self addAttribute:NSFontAttributeName value:font range:range]; - }]; } [self deleteCharactersInRange:[result rangeAtIndex:6]]; [self deleteCharactersInRange:[result rangeAtIndex:1]]; @@ -113,7 +134,7 @@ - (void)formatMarkDown { } - (CGFloat)annotateRubyInRange:(NSRange)range - verticalLayout:(BOOL)isVertical + verticalOrientation:(BOOL)isVertical maximumLength:(CGFloat)maxLength { NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: @"(\uFFF9\\s*)(\\S+?)(\\s*\uFFFA(.+?)\uFFFB)" options:0 error:nil]; @@ -125,70 +146,153 @@ - (CGFloat)annotateRubyInRange:(NSRange)range NSRange baseRange = [result rangeAtIndex:2]; // no ruby annotation if the base string includes line breaks if ([self attributedSubstringFromRange:NSMakeRange(0, NSMaxRange(baseRange))].size.width > maxLength) { - [self deleteCharactersInRange:NSMakeRange(NSMaxRange([result range]) - 1, 1)]; + [self deleteCharactersInRange:NSMakeRange(NSMaxRange(result.range) - 1, 1)]; [self deleteCharactersInRange:NSMakeRange([result rangeAtIndex:3].location, 1)]; [self deleteCharactersInRange:NSMakeRange([result rangeAtIndex:1].location, 1)]; offset -= 3; } else { // base string must use only one font so that all fall within one glyph run and the ruby annotation is aligned with no duplicates - NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:baseRange.location effectiveRange:NULL]; - baseFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)baseFont, (CFStringRef)self.string, - CFRangeMake((int)baseRange.location, (int)baseRange.length))); + NSFont *baseFont = [self attribute:NSFontAttributeName + atIndex:baseRange.location + effectiveRange:NULL]; + baseFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)baseFont, (CFStringRef)self.string, + CFRangeMake((CFIndex)baseRange.location, (CFIndex)baseRange.length), CFSTR("zh"))); [self addAttribute:NSFontAttributeName value:baseFont range:baseRange]; CGFloat rubyScale = 0.5; CFStringRef rubyString = (__bridge CFStringRef)[self.string substringWithRange:[result rangeAtIndex:4]]; - NSFont *rubyFont = [self attribute:NSFontAttributeName atIndex:[result rangeAtIndex:4].location effectiveRange:NULL]; - rubyFont = [NSFont fontWithDescriptor:rubyFont.fontDescriptor size:rubyFont.pointSize * rubyScale]; - rubyFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)rubyFont, rubyString, - CFRangeMake(0, CFStringGetLength(rubyString)))); - rubyLineHeight = MAX(rubyLineHeight, isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender : rubyFont.ascender - rubyFont.descender); + NSFont *rubyFont = [self attribute:NSFontAttributeName + atIndex:[result rangeAtIndex:4].location + effectiveRange:NULL]; + rubyFont = [NSFont fontWithDescriptor:rubyFont.fontDescriptor + size:rubyFont.pointSize * rubyScale]; + rubyFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)rubyFont, rubyString, + CFRangeMake(0, CFStringGetLength(rubyString)), CFSTR("zh"))); + rubyLineHeight = MAX(rubyLineHeight, isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender + : rubyFont.ascender - rubyFont.descender); CGColorRef rubyColor = [[self attribute:NSForegroundColorAttributeName - atIndex:[result rangeAtIndex:4].location effectiveRange:NULL] CGColor]; - CGFloat rubyBaselineOffset; - if (@available(macOS 12.0, *)) { - rubyBaselineOffset = isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender : -rubyFont.descender; - } else { - rubyBaselineOffset = isVertical ? rubyFont.verticalFont.ascender : -rubyFont.descender; - } + atIndex:[result rangeAtIndex:4].location + effectiveRange:NULL] CGColor]; CFTypeRef keys[] = {kCTFontAttributeName, kCTForegroundColorAttributeName, - kCTBaselineOffsetAttributeName, kCTRubyAnnotationSizeFactorAttributeName, - kCTRubyAnnotationScaleToFitAttributeName}; - CFTypeRef values[] = {(__bridge CTFontRef)rubyFont, rubyColor, - CFNumberCreate(NULL, kCFNumberDoubleType, &rubyBaselineOffset), - CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), kCFBooleanFalse}; + kCTBaselineClassAttributeName, kCTRubyAnnotationSizeFactorAttributeName, + kCTRubyAnnotationScaleToFitAttributeName}; + CFTypeRef values[] = {(__bridge CTFontRef)rubyFont, rubyColor, kCTBaselineClassIdeographicHigh, + CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), kCFBooleanFalse}; CFDictionaryRef rubyAttrs = CFDictionaryCreate(NULL, keys, values, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, - kCTRubyOverhangAuto, kCTRubyPositionBefore, rubyString, rubyAttrs); + kCTRubyOverhangNone, kCTRubyPositionBefore, rubyString, rubyAttrs); [self deleteCharactersInRange:[result rangeAtIndex:3]]; if (@available(macOS 12.0, *)) { - [self addAttributes:@{CFBridgingRelease(kCTRubyAnnotationAttributeName): CFBridgingRelease(rubyAnnotation), - NSVerticalGlyphFormAttributeName: @(isVertical)} range:baseRange]; + [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName : CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName : @(isVertical)} + range:baseRange]; [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:3].length + [result rangeAtIndex:1].length; } else { // use U+008B as placeholder for line-forward spaces in case ruby is wider than base - [self replaceCharactersInRange:NSMakeRange(NSMaxRange(baseRange), 0) withString:[NSString stringWithFormat:@"%C", 0x8B]]; + [self replaceCharactersInRange:NSMakeRange(NSMaxRange(baseRange), 0) + withString:[NSString stringWithFormat:@"%C", 0x8B]]; baseRange.length += 1; - [self addAttributes:@{CFBridgingRelease(kCTRubyAnnotationAttributeName): CFBridgingRelease(rubyAnnotation), - NSVerticalGlyphFormAttributeName: @(isVertical)} range:baseRange]; + [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName : CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName : @(isVertical)} + range:baseRange]; [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:3].length - 1 + [result rangeAtIndex:1].length; } } }]; - return rubyLineHeight; + if (offset == 0) { + [self.mutableString replaceOccurrencesOfString:@"[\uFFF9-\uFFFB]" + withString:@"" + options:NSRegularExpressionSearch + range:range]; + } + return ceil(rubyLineHeight); } @end // NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) +@implementation NSColorSpace (labColorSpace) + ++ (NSColorSpace *)labColorSpace { + CGFloat whitePoint[3] = {0.950489, 1.0, 1.088840}; + CGFloat blackPoint[3] = {0.0, 0.0, 0.0}; + CGFloat range[4] = {-127.0, 127.0, -127.0, 127.0}; + CGColorSpaceRef colorSpaceLab = CGColorSpaceCreateLab(whitePoint, blackPoint, range); + NSColorSpace *labColorSpace = [[NSColorSpace alloc] initWithCGColorSpace:colorSpaceLab]; + CGColorSpaceRelease(colorSpaceLab); + return labColorSpace; +} + +@end // NSColorSpace (labColorSpace) + + +@implementation NSColor (colorWithLabColorSpace) + ++ (NSColor *)colorWithLabLuminance:(CGFloat)luminance + a:(CGFloat)a + b:(CGFloat)b + alpha:(CGFloat)alpha { + luminance = MAX(MIN(luminance, 100.0), 0.0); + a = MAX(MIN(a, 127.0), -127.0); + b = MAX(MIN(b, 127.0), -127.0); + alpha = MAX(MIN(alpha, 1.0), 0.0); + CGFloat components[4] = {luminance, a, b, alpha}; + return [NSColor colorWithColorSpace:NSColorSpace.labColorSpace + components:components count:4]; +} + +- (void)getLuminance:(CGFloat *)luminance + a:(CGFloat *)a + b:(CGFloat *)b + alpha:(CGFloat *)alpha { + NSColor *labColor = [self colorUsingColorSpace:NSColorSpace.labColorSpace]; + CGFloat components[4] = {0.0, 0.0, 0.0, 1.0}; + [labColor getComponents:components]; + *luminance = components[0] / 100.0; + *a = components[1] / 127.0; // green-red + *b = components[2] / 127.0; // blue-yellow + *alpha = components[3]; +} + +- (CGFloat)luminanceComponent { + NSColor *labColor = [self colorUsingColorSpace:NSColorSpace.labColorSpace]; + CGFloat components[4] = {0.0, 0.0, 0.0, 1.0}; + [labColor getComponents:components]; + return components[0] / 100.0; +} + +- (NSColor *)invertLuminanceWithAdjustment:(NSInteger)sign { + if (self == nil) { + return nil; + } + NSColor *labColor = [self colorUsingColorSpace:NSColorSpace.labColorSpace]; + CGFloat components[4] = {0.0, 0.0, 0.0, 1.0}; + [labColor getComponents:components]; + BOOL isDark = components[0] < 60; + if (sign > 0) { + components[0] = isDark ? 100.0 - components[0] * 2.0 / 3.0 : 150.0 - components[0] * 1.5; + } else if (sign < 0) { + components[0] = isDark ? 80.0 - components[0] / 3.0 : 135.0 - components[0] * 1.25; + } else { + components[0] = isDark ? 90.0 - components[0] / 2.0 : 120.0 - components[0]; + } + NSColor *invertedColor = [NSColor colorWithColorSpace:NSColorSpace.labColorSpace + components:components count:4]; + return [invertedColor colorUsingColorSpace:self.colorSpace]; +} + +@end // NSColor (colorWithLabColorSpace) + +#pragma mark - Color scheme and other user configurations + @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSColor *backgroundImage; +@property(nonatomic, strong, readonly) NSImage *backgroundImage; @property(nonatomic, strong, readonly) NSColor *highlightedStripColor; @property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; @property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; @@ -232,6 +336,8 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSAttributedString *symbolBackStroke; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; +@property(nonatomic, strong, readonly) NSAttributedString *symbolDeleteFill; +@property(nonatomic, strong, readonly) NSAttributedString *symbolDeleteStroke; @property(nonatomic, strong, readonly) NSString *selectKeys; @property(nonatomic, strong, readonly) NSString *candidateFormat; @@ -241,7 +347,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSString *statusMessageType; - (void)setBackgroundColor:(NSColor *)backgroundColor - backgroundImage:(NSColor *)backgroundImage + backgroundImage:(NSImage *)backgroundImage highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor @@ -297,93 +403,109 @@ - (void)setAnnotationHeight:(CGFloat)height; @implementation SquirrelTheme -static NSArray * formatLabels(NSAttributedString *format, NSArray *labels) { +static NSArray * formatLabels(NSAttributedString *format, + NSArray *labels) { NSRange enumRange = NSMakeRange(0, 0); - NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; - NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString:[labels componentsJoinedByString:@""]]; - if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff10, 10)] + NSMutableArray *formatted = + [[NSMutableArray alloc] initWithCapacity:labels.count]; + NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString: + [labels componentsJoinedByString:@""]]; + if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xFF10, 10)] isSupersetOfSet:labelCharacters]) { // 01..9 - if ([format.string containsString:@"%c\u20dd"]) { // ①..⑨⓪ - enumRange = [format.string rangeOfString:@"%c\u20dd"]; + if ([format.string containsString:@"%c\u20E3"]) { // 1⃣..9⃣0⃣ + enumRange = [format.string rangeOfString:@"%c\u20E3"]; for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x24ea : [label characterAtIndex:0] - 0xff11 + 0x2460, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {[label characterAtIndex:0] - 0xFF10 + 0x0030, 0xFE0E, 0x20E3, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; + } + } else if ([format.string containsString:@"%c\u20DD"]) { // ①..⑨⓪ + enumRange = [format.string rangeOfString:@"%c\u20DD"]; + for (NSString *label in labels) { + const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0x24EA : + [label characterAtIndex:0] - 0xFF11 + 0x2460, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } } else if ([format.string containsString:@"(%c)"]) { // ⑴..⑼⑽ enumRange = [format.string rangeOfString:@"(%c)"]; for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] == 0xff10 ? 0x247d : [label characterAtIndex:0] - 0xff11 + 0x2474, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0x247D : + [label characterAtIndex:0] - 0xFF11 + 0x2474, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } } else if ([format.string containsString:@"%c."]) { // ⒈..⒐🄀 enumRange = [format.string rangeOfString:@"%c."]; for (NSString *label in labels) { - if ([label characterAtIndex:0] == 0xff10) { - unichar chars[] = {0xd83c, 0xdd00, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:3]]; - [formatted addObject:[newFormat copy]]; - } else { - unichar chars[] = {[label characterAtIndex:0] - 0xff11 + 0x2488, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; - } + const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0xD83C : + [label characterAtIndex:0] - 0xFF11 + 0x2488, + [label characterAtIndex:0] == 0xFF10 ? 0xDD00 : 0x0, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } - } else if ([format.string containsString:@"%c,"]) { //🄂..🄊🄁 + } else if ([format.string containsString:@"%c,"]) { // 🄂..🄊🄁 enumRange = [format.string rangeOfString:@"%c,"]; for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff10 + 0xdd01, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {0xD83C, [label characterAtIndex:0] - 0xFF10 + 0xDD01, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } } - } else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xff21, 26)] + } else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xFF21, 26)] isSupersetOfSet:labelCharacters]) { // A..Z - if ([format.string containsString:@"%c\u20dd"]) { // Ⓐ..Ⓩ - enumRange = [format.string rangeOfString:@"%c\u20dd"]; + if ([format.string containsString:@"%c\u20DD"]) { // Ⓐ..Ⓩ + enumRange = [format.string rangeOfString:@"%c\u20DD"]; for (NSString *label in labels) { - unichar chars[] = {[label characterAtIndex:0] - 0xff21 + 0x24b6, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {[label characterAtIndex:0] - 0xFF21 + 0x24B6, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } } else if ([format.string containsString:@"(%c)"]) { // 🄐..🄩 enumRange = [format.string rangeOfString:@"(%c)"]; for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd10, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {0xD83C, [label characterAtIndex:0] - 0xFF21 + 0xDD10, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } - } else if ([format.string containsString:@"%c\u20de"]) { // 🄰..🅉 - enumRange = [format.string rangeOfString:@"%c\u20de"]; + } else if ([format.string containsString:@"%c\u20DE"]) { // 🄰..🅉 + enumRange = [format.string rangeOfString:@"%c\u20DE"]; for (NSString *label in labels) { - unichar chars[] = {0xd83c, [label characterAtIndex:0] - 0xff21 + 0xdd30, 0x0}; - NSMutableAttributedString *newFormat = [format mutableCopy]; - [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithCharacters:chars length:2]]; - [formatted addObject:[newFormat copy]]; + const unichar chars[] = {0xD83C, [label characterAtIndex:0] - 0xFF21 + 0xDD30, 0x0}; + NSMutableAttributedString *newFormat = format.mutableCopy; + [newFormat replaceCharactersInRange:enumRange + withString:[NSString stringWithFormat:@"%S", chars]]; + [formatted addObject:newFormat]; } } } if (enumRange.length == 0) { enumRange = [format.string rangeOfString:@"%c"]; for (NSString *label in labels) { - NSMutableAttributedString *newFormat = [format mutableCopy]; + NSMutableAttributedString *newFormat = format.mutableCopy; [newFormat replaceCharactersInRange:enumRange withString:label]; - [formatted addObject:[newFormat copy]]; + [formatted addObject:newFormat]; } } - return [formatted copy]; + return formatted; } - (void)setBackgroundColor:(NSColor *)backgroundColor - backgroundImage:(NSColor *)backgroundImage + backgroundImage:(NSImage *)backgroundImage highlightedStripColor:(NSColor *)highlightedStripColor highlightedPreeditColor:(NSColor *)highlightedPreeditColor preeditBackgroundColor:(NSColor *)preeditBackgroundColor @@ -453,68 +575,67 @@ - (void) setAttrs:(NSDictionary *)attrs _pagingHighlightedAttrs = pagingHighlightedAttrs; _statusAttrs = statusAttrs; - NSMutableDictionary *sepAttrs = [commentAttrs mutableCopy]; + NSMutableDictionary *sepAttrs = commentAttrs.mutableCopy; sepAttrs[NSVerticalGlyphFormAttributeName] = @(NO); - _separator = [[NSAttributedString alloc] initWithString:_linear ? (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" attributes:sepAttrs]; - - NSMutableDictionary *symbolAttrs = [pagingAttrs mutableCopy]; - if (@available(macOS 12.0, *)) { - NSTextAttachment *attmLeftFill = [[NSTextAttachment alloc] init]; - attmLeftFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.left.circle.fill" accessibilityDescription:nil]; - NSTextAttachment *attmLeftStroke = [[NSTextAttachment alloc] init]; - attmLeftStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.left.circle" accessibilityDescription:nil]; - NSTextAttachment *attmRightFill = [[NSTextAttachment alloc] init]; - attmRightFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.right.circle.fill" accessibilityDescription:nil]; - NSTextAttachment *attmRightStroke = [[NSTextAttachment alloc] init]; - attmRightStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.right.circle" accessibilityDescription:nil]; - NSTextAttachment *attmUpFill = [[NSTextAttachment alloc] init]; - attmUpFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.up.circle.fill" accessibilityDescription:nil]; - NSTextAttachment *attmUpStroke = [[NSTextAttachment alloc] init]; - attmUpStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.up.circle" accessibilityDescription:nil]; - NSTextAttachment *attmDownFill = [[NSTextAttachment alloc] init]; - attmDownFill.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.down.circle.fill" accessibilityDescription:nil]; - NSTextAttachment *attmDownStroke = [[NSTextAttachment alloc] init]; - attmDownStroke.image = [NSImage imageWithSystemSymbolName:@"arrowtriangle.down.circle" accessibilityDescription:nil]; - - NSMutableDictionary *attrsBackFill = [symbolAttrs mutableCopy]; - attrsBackFill[NSAttachmentAttributeName] = _linear ? attmUpFill : attmLeftFill; - _symbolBackFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackFill]; - NSMutableDictionary *attrsBackStroke = [symbolAttrs mutableCopy]; - attrsBackStroke[NSAttachmentAttributeName] = _linear ? attmUpStroke : attmLeftStroke; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackStroke]; - NSMutableDictionary *attrsForwardFill = [symbolAttrs mutableCopy]; - attrsForwardFill[NSAttachmentAttributeName] = _linear ? attmDownFill : attmRightFill; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardFill]; - NSMutableDictionary *attrsForwardStroke = [symbolAttrs mutableCopy]; - attrsForwardStroke[NSAttachmentAttributeName] = _linear ? attmDownStroke : attmRightStroke; - _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardStroke]; - } else { - NSFont *symbolFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0.0] - fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:[labelAttrs[NSFontAttributeName] pointSize]]; - if (_linear) { - CGAffineTransform transform = CGAffineTransformMakeRotation(-M_PI_2); - CTFontRef rotatedSymbolFont = CTFontCreateCopyWithSymbolicTraits((CTFontRef)symbolFont, symbolFont.pointSize, &transform, kCTFontTraitVertical, kCTFontTraitClassMask); - symbolAttrs[NSFontAttributeName] = CFBridgingRelease(rotatedSymbolFont); - symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] + symbolFont.ascender); - symbolAttrs[CFBridgingRelease(kCTTrackingAttributeName)] = @(symbolFont.ascender); - } else { - symbolAttrs[NSFontAttributeName] = symbolFont; - symbolAttrs[NSBaselineOffsetAttributeName] = @([pagingAttrs[NSBaselineOffsetAttributeName] doubleValue] - symbolFont.leading); - } - NSMutableDictionary *symbolAttrsBackFill = [symbolAttrs mutableCopy]; - NSMutableDictionary *symbolAttrsBackStroke = [symbolAttrs mutableCopy]; - NSMutableDictionary *symbolAttrsForwardFill = [symbolAttrs mutableCopy]; - NSMutableDictionary *symbolAttrsForwardStroke = [symbolAttrs mutableCopy]; - symbolAttrsBackFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE92 forFont:symbolFont baseString:@"◀"]; //gid4966 - symbolAttrsBackStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE95 forFont:symbolFont baseString:@"◁"]; //gid4969 - symbolAttrsForwardFill[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE93 forFont:symbolFont baseString:@"▶"]; //gid4967 - symbolAttrsForwardStroke[NSGlyphInfoAttributeName] = [NSGlyphInfo glyphInfoWithCGGlyph:0xE94 forFont:symbolFont baseString:@"▷"]; //gid4968 - _symbolBackFill = [[NSAttributedString alloc] initWithString:@"◀" attributes:symbolAttrsBackFill]; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"◁" attributes:symbolAttrsBackStroke]; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:@"▶" attributes:symbolAttrsForwardFill]; - _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"▷" attributes:symbolAttrsForwardStroke]; - } + sepAttrs[NSKernAttributeName] = @(0.0); + _separator = [[NSAttributedString alloc] initWithString:_linear ? + (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" + attributes:sepAttrs]; + + // Symbols for function buttons + NSTextAttachment *attmLeftFill = [[NSTextAttachment alloc] init]; + attmLeftFill.image = [NSImage imageNamed:@"Symbols/chevron.left.circle.fill"]; + NSTextAttachment *attmUpFill = [[NSTextAttachment alloc] init]; + attmUpFill.image = [NSImage imageNamed:@"Symbols/chevron.up.circle.fill"]; + NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; + attrsBackFill[NSAttachmentAttributeName] = _linear ? attmUpFill : attmLeftFill; + _symbolBackFill = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsBackFill]; + + NSTextAttachment *attmLeftStroke = [[NSTextAttachment alloc] init]; + attmLeftStroke.image = [NSImage imageNamed:@"Symbols/chevron.left.circle"]; + NSTextAttachment *attmUpStroke = [[NSTextAttachment alloc] init]; + attmUpStroke.image = [NSImage imageNamed:@"Symbols/chevron.up.circle"]; + NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; + attrsBackStroke[NSAttachmentAttributeName] = _linear ? attmUpStroke : attmLeftStroke; + _symbolBackStroke = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsBackStroke]; + + NSTextAttachment *attmRightFill = [[NSTextAttachment alloc] init]; + attmRightFill.image = [NSImage imageNamed:@"Symbols/chevron.right.circle.fill"]; + NSTextAttachment *attmDownFill = [[NSTextAttachment alloc] init]; + attmDownFill.image = [NSImage imageNamed:@"Symbols/chevron.down.circle.fill"]; + NSMutableDictionary *attrsForwardFill = pagingAttrs.mutableCopy; + attrsForwardFill[NSAttachmentAttributeName] = _linear ? attmDownFill : attmRightFill; + _symbolForwardFill = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsForwardFill]; + + NSTextAttachment *attmRightStroke = [[NSTextAttachment alloc] init]; + attmRightStroke.image = [NSImage imageNamed:@"Symbols/chevron.right.circle"]; + NSTextAttachment *attmDownStroke = [[NSTextAttachment alloc] init]; + attmDownStroke.image = [NSImage imageNamed:@"Symbols/chevron.down.circle"]; + NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; + attrsForwardStroke[NSAttachmentAttributeName] = _linear ? attmDownStroke : attmRightStroke; + _symbolForwardStroke = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsForwardStroke]; + + NSTextAttachment *attmDeleteFill = [[NSTextAttachment alloc] init]; + attmDeleteFill.image = [NSImage imageNamed:@"Symbols/delete.backward.fill"]; + NSMutableDictionary *attrsDeleteFill = preeditAttrs.mutableCopy; + attrsDeleteFill[NSAttachmentAttributeName] = attmDeleteFill; + attrsDeleteFill[NSVerticalGlyphFormAttributeName] = @(NO); + attrsDeleteFill[NSKernAttributeName] = @(0.0); + _symbolDeleteFill = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsDeleteFill]; + + NSTextAttachment *attmDeleteStroke = [[NSTextAttachment alloc] init]; + attmDeleteStroke.image = [NSImage imageNamed:@"Symbols/delete.backward"]; + NSMutableDictionary *attrsDeleteStroke = preeditAttrs.mutableCopy; + attrsDeleteStroke[NSAttachmentAttributeName] = attmDeleteStroke; + attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @(NO); + attrsDeleteStroke[NSKernAttributeName] = @(0.0); + _symbolDeleteStroke = [[NSAttributedString alloc] + initWithString:@"\uFFFC" attributes:attrsDeleteStroke]; } - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -544,24 +665,25 @@ - (void)setCandidateFormat:(NSString *)candidateFormat { - (void)updateCandidateFormats { // validate candidate format: must have enumerator '%c' before candidate '%@' - NSMutableString *candidateFormat = [_candidateFormat mutableCopy]; + NSMutableString *candidateFormat = _candidateFormat.mutableCopy; if (![candidateFormat containsString:@"%@"]) { [candidateFormat appendString:@"%@"]; } - if (![candidateFormat containsString:@"%c"]) { + NSRange labelRange = [candidateFormat rangeOfString:@"%c" options:NSLiteralSearch]; + if (labelRange.length == 0) { [candidateFormat insertString:@"%c" atIndex:0]; } NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; - NSRange labelRange = [candidateFormat rangeOfString:@"%c"]; if (labelRange.location > candidateRange.location) { [candidateFormat setString:kDefaultCandidateFormat]; candidateRange = [candidateFormat rangeOfString:@"%@"]; } labelRange = NSMakeRange(0, candidateRange.location); - NSRange commentRange = NSMakeRange(NSMaxRange(candidateRange), candidateFormat.length - NSMaxRange(candidateRange)); + NSRange commentRange = NSMakeRange(NSMaxRange(candidateRange), + candidateFormat.length - NSMaxRange(candidateRange)); // parse markdown formats NSMutableAttributedString *format = [[NSMutableAttributedString alloc] initWithString:candidateFormat]; - NSMutableAttributedString *highlightedFormat = [format mutableCopy]; + NSMutableAttributedString *highlightedFormat = format.mutableCopy; [format addAttributes:_labelAttrs range:labelRange]; [highlightedFormat addAttributes:_labelHighlightedAttrs range:labelRange]; [format addAttributes:_attrs range:candidateRange]; @@ -574,13 +696,18 @@ - (void)updateCandidateFormats { [highlightedFormat formatMarkDown]; // add placeholder for comment '%s' candidateRange = [format.string rangeOfString:@"%@"]; - commentRange = NSMakeRange(NSMaxRange(candidateRange), format.length - NSMaxRange(candidateRange)); + commentRange = NSMakeRange(NSMaxRange(candidateRange), + format.length - NSMaxRange(candidateRange)); if (commentRange.length > 0) { - [format replaceCharactersInRange:commentRange withString:[kTipSpecifier stringByAppendingString:[format.string substringWithRange:commentRange]]]; - [highlightedFormat replaceCharactersInRange:commentRange withString:[kTipSpecifier stringByAppendingString:[highlightedFormat.string substringWithRange:commentRange]]]; + [format replaceCharactersInRange:commentRange withString: + [kTipSpecifier stringByAppendingString:[format.string substringWithRange:commentRange]]]; + [highlightedFormat replaceCharactersInRange:commentRange withString: + [kTipSpecifier stringByAppendingString:[highlightedFormat.string substringWithRange:commentRange]]]; } else { - [format appendAttributedString:[[NSAttributedString alloc] initWithString:kTipSpecifier attributes:_commentAttrs]]; - [highlightedFormat appendAttributedString:[[NSAttributedString alloc] initWithString:kTipSpecifier attributes:_commentHighlightedAttrs]]; + [format appendAttributedString:[[NSAttributedString alloc] initWithString: + kTipSpecifier attributes:_commentAttrs]]; + [highlightedFormat appendAttributedString:[[NSAttributedString alloc] initWithString: + kTipSpecifier attributes:_commentHighlightedAttrs]]; } _candidateFormats = formatLabels(format, _labels); _candidateHighlightedFormats = formatLabels(highlightedFormat, _labels); @@ -597,7 +724,7 @@ - (void)setStatusMessageType:(NSString *)type { - (void)setAnnotationHeight:(CGFloat)height { if (height > 0 && _linespace < height * 2) { _linespace = height * 2; - NSMutableParagraphStyle *paragraphStyle = [_paragraphStyle mutableCopy]; + NSMutableParagraphStyle *paragraphStyle = _paragraphStyle.mutableCopy; paragraphStyle.paragraphSpacingBefore = height; paragraphStyle.paragraphSpacing = height; _paragraphStyle = paragraphStyle; @@ -606,6 +733,7 @@ - (void)setAnnotationHeight:(CGFloat)height { @end // SquirrelTheme +#pragma mark - Typesetting extensions for TextKit 1 (macOS 11 or lower) @interface SquirrelLayoutManager : NSLayoutManager @end @@ -615,44 +743,117 @@ @implementation SquirrelLayoutManager - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange atPoint:(NSPoint)origin { NSRange charRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; - NSTextContainer *textContainer = [self textContainerForGlyphAtIndex:glyphRange.location effectiveRange:NULL withoutAdditionalLayout:YES]; - CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; + NSTextContainer *textContainer = [self textContainerForGlyphAtIndex:glyphRange.location + effectiveRange:NULL withoutAdditionalLayout:YES]; + BOOL verticalOrientation = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + CGContextRef context = NSGraphicsContext.currentContext.CGContext; CGContextResetClip(context); - [self.textStorage enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - NSRange glyRange = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - if (attrs[CFBridgingRelease(kCTRubyAnnotationAttributeName)]) { - CGContextSaveGState(context); + [self.textStorage + enumerateAttributesInRange:charRange + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired + usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSRange glyRange = [self glyphRangeForCharacterRange:range + actualCharacterRange:NULL]; + NSRect lineRect = [self lineFragmentRectForGlyphAtIndex:glyRange.location + effectiveRange:NULL + withoutAdditionalLayout:YES]; + CGContextSaveGState(context); + if (attrs[(NSString *)kCTRubyAnnotationAttributeName]) { CGContextScaleCTM(context, 1.0, -1.0); NSUInteger glyphIndex = glyRange.location; - NSRect lineRect = [self lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL withoutAdditionalLayout:YES]; - CTLineRef line = CTLineCreateWithAttributedString( - (CFAttributedStringRef)[self.textStorage attributedSubstringFromRange:range]); + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) + [self.textStorage attributedSubstringFromRange:range]); CFArrayRef runs = CTLineGetGlyphRuns(line); for (CFIndex i = 0; i < CFArrayGetCount(runs); ++i) { CGPoint position = [self locationForGlyphAtIndex:glyphIndex]; + NSPoint backingTextPosition = [textContainer.textView convertPointToBacking: + NSMakePoint(lineRect.origin.x + position.x, + lineRect.origin.y + position.y)]; + NSPoint textPosition = [textContainer.textView convertPointFromBacking: + NSMakePoint(round(backingTextPosition.x), + round(backingTextPosition.y))]; CTRunRef run = CFArrayGetValueAtIndex(runs, i); CGAffineTransform matrix = CTRunGetTextMatrix(run); - matrix.tx = origin.x + NSMinX(lineRect) + position.x; - matrix.ty = - origin.y - NSMinY(lineRect) - position.y; + matrix.tx = textPosition.x; + matrix.ty = -textPosition.y; CGContextSetTextMatrix(context, matrix); CTRunDraw(run, context, CFRangeMake(0, 0)); glyphIndex += (NSUInteger)CTRunGetGlyphCount(run); } - CGContextRestoreGState(context); CFRelease(line); } else { - [super drawGlyphsForGlyphRange:glyRange atPoint:origin]; + NSPoint position = [self locationForGlyphAtIndex:glyRange.location]; + position.x += origin.x + lineRect.origin.x; + position.y += origin.y + lineRect.origin.y; + CGContextSetTextPosition(context, position.x, position.y); + NSFont *runFont = attrs[NSFontAttributeName]; + NSPoint glyphOffset = NSZeroPoint; + if (verticalOrientation && runFont.pointSize < 24 && + [runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; + glyphOffset.x = runFont.capHeight - runFont.pointSize; + glyphOffset.y = (runFont.capHeight - runFont.pointSize) * + (superscript == 0 ? 0.5 : (superscript == 1 ? 1.0 / 0.55 - 0.55 : 0.0)); + } + NSPoint backingGlyphOrigin = [textContainer.textView convertPointToBacking: + NSMakePoint(position.x + glyphOffset.x, + position.y + glyphOffset.y)]; + NSPoint glyphOrigin = [textContainer.textView convertPointFromBacking: + NSMakePoint(round(backingGlyphOrigin.x), + round(backingGlyphOrigin.y))]; + [super drawGlyphsForGlyphRange:glyRange + atPoint:NSMakePoint(glyphOrigin.x - position.x, + glyphOrigin.y - position.y)]; } + CGContextRestoreGState(context); }]; CGContextClipToRect(context, textContainer.textView.superview.bounds); } +- (BOOL) layoutManager:(NSLayoutManager *)layoutManager + shouldSetLineFragmentRect:(inout NSRect *)lineFragmentRect + lineFragmentUsedRect:(inout NSRect *)lineFragmentUsedRect + baselineOffset:(inout CGFloat *)baselineOffset + inTextContainer:(NSTextContainer *)textContainer + forGlyphRange:(NSRange)glyphRange { + BOOL verticalOrientation = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + NSRange charRange = [layoutManager characterRangeForGlyphRange:glyphRange + actualGlyphRange:NULL]; + NSParagraphStyle *style = [layoutManager.textStorage attribute:NSParagraphStyleAttributeName + atIndex:charRange.location + effectiveRange:NULL]; + NSFont *refFont = [layoutManager.textStorage attribute:(NSString *)kCTBaselineReferenceInfoAttributeName + atIndex:charRange.location + effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; + CGFloat refFontHeight = refFont.ascender - refFont.descender; + CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, + style.minimumLineHeight); + lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; + *lineFragmentRect = [textContainer.textView backingAlignedRect:*lineFragmentRect + options:NSAlignAllEdgesNearest]; + *lineFragmentUsedRect = [textContainer.textView backingAlignedRect:*lineFragmentUsedRect + options:NSAlignAllEdgesNearest]; + NSRect lineFragmentAscentRect = *lineFragmentRect; + lineFragmentAscentRect.size.height = lineFragmentUsedRect->origin.y - lineFragmentRect->origin.y + + (verticalOrientation ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); + lineFragmentAscentRect = [textContainer.textView backingAlignedRect:lineFragmentAscentRect + options:NSAlignAllEdgesNearest]; + *baselineOffset = lineFragmentAscentRect.size.height; + return YES; +} + +- (BOOL) layoutManager:(NSLayoutManager *)layoutManager + shouldBreakLineByWordBeforeCharacterAtIndex:(NSUInteger)charIndex { + return charIndex <= 1 || [layoutManager.textStorage.string characterAtIndex:charIndex - 1] != '\t'; +} + - (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager shouldUseAction:(NSControlCharacterAction)action forControlCharacterAtIndex:(NSUInteger)charIndex { if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B && - [layoutManager.textStorage attribute:CFBridgingRelease(kCTRubyAnnotationAttributeName) atIndex:charIndex effectiveRange:NULL]) { + [layoutManager.textStorage attribute:(NSString *)kCTRubyAnnotationAttributeName + atIndex:charIndex + effectiveRange:NULL]) { return NSControlCharacterActionWhitespace; } else { return action; @@ -668,7 +869,9 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager CGFloat width = 0.0; if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B) { NSRange rubyRange; - id rubyAnnotation = [layoutManager.textStorage attribute:CFBridgingRelease(kCTRubyAnnotationAttributeName) atIndex:charIndex effectiveRange:&rubyRange]; + id rubyAnnotation = [layoutManager.textStorage attribute:(NSString *)kCTRubyAnnotationAttributeName + atIndex:charIndex + effectiveRange:&rubyRange]; if (rubyAnnotation) { NSAttributedString *rubyString = [layoutManager.textStorage attributedSubstringFromRange:rubyRange]; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)rubyString); @@ -683,6 +886,7 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager @end // SquirrelLayoutManager +#pragma mark - Typesetting extensions for TextKit 2 (macOS 12 or lower) API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutFragment : NSTextLayoutFragment @@ -692,16 +896,19 @@ @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { - BOOL vertical = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; - NSArray *lineFragments = self.textLineFragments; - for (NSTextLineFragment *lineFrag in lineFragments) { - NSFont *refFont = [lineFrag.attributedString - attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 - effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - CGPoint renderOrigin = CGPointMake(point.x + NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, - point.y + NSMidY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + - (vertical ? 0.0 : refFont.ascender / 2 + refFont.descender / 2)); + BOOL verticalOrientation = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + for (NSTextLineFragment *lineFrag in self.textLineFragments) { + CGContextSaveGState(context); + NSFont *refFont = [lineFrag.attributedString attribute:(NSString *)kCTBaselineReferenceInfoAttributeName + atIndex:lineFrag.characterRange.location + effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; + CGPoint renderOrigin = CGPointMake(NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, + NSMidY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + + (verticalOrientation ? 0.0 : refFont.ascender / 2 + refFont.descender / 2)); + CGPoint deviceRenderOrigin = CGContextConvertPointToDeviceSpace(context, renderOrigin); + renderOrigin = CGContextConvertPointToUserSpace(context, CGPointMake(round(deviceRenderOrigin.x), round(deviceRenderOrigin.y))); [lineFrag drawAtPoint:renderOrigin inContext:context]; + CGContextRestoreGState(context); } } @@ -714,42 +921,63 @@ @interface SquirrelTextLayoutManager : NSTextLayoutManager )location + hyphenating:(BOOL)hyphenating { + NSTextContentStorage *contentStorage = textLayoutManager.textContainer.textView.textContentStorage; + NSInteger charIndex = [contentStorage offsetFromLocation:contentStorage.documentRange.location + toLocation:location]; + return charIndex <= 1 || [contentStorage.textStorage.string characterAtIndex:(NSUInteger)charIndex - 1] != '\t'; +} + - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutManager textLayoutFragmentForLocation:(id)location inTextElement:(NSTextElement *)textElement { - return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement range:textElement.elementRange]; + return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement + range:[[NSTextRange alloc] initWithLocation:location + endLocation:textElement.elementRange.endLocation]]; } @end // SquirrelTextLayoutManager +#pragma mark - View behind text, containing drawings of backgrounds and highlights @interface SquirrelView : NSView @property(nonatomic, readonly) NSTextView *textView; @property(nonatomic, readonly) NSTextStorage *textStorage; -@property(nonatomic, readonly) NSEdgeInsets insets; +@property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; +@property(nonatomic, readonly) CAShapeLayer *shape; +@property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) SquirrelAppear appear; +@property(nonatomic, readonly) NSEdgeInsets alignmentRectInsets; @property(nonatomic, readonly) NSArray *candidateRanges; -@property(nonatomic, readonly) NSUInteger highlightedIndex; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; -@property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) NSUInteger highlightedIndex; +@property(nonatomic, readonly) rimeIndex functionButton; +@property(nonatomic, readonly) NSBezierPath *deleteBackPath; @property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingPaths; -@property(nonatomic, readonly) NSUInteger pagingButton; -@property(nonatomic, readonly) CAShapeLayer *shape; -@property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; -@property(nonatomic, readonly) SquirrelAppear appear; -- (void)drawViewWithInsets:(NSEdgeInsets)insets +- (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(macos(12.0)); + +- (NSRange)getCharRangeFromTextRange:(NSTextRange *)textRange API_AVAILABLE(macos(12.0)); + +- (NSRect)contentRectForRange:(NSRange)range; + +- (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets candidateRanges:(NSArray *)candidateRanges highlightedIndex:(NSUInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton; + pagingRange:(NSRange)pagingRange; -- (NSRect)contentRectForRange:(NSRange)range; +- (void)highlightFunctionButton:(rimeIndex)functionButton; + +- (BOOL)convertClickSpot:(NSPoint)spot + toIndex:(NSUInteger *)index; @end @@ -792,13 +1020,13 @@ - (instancetype)initWithFrame:(NSRect)frameRect { if (self) { self.wantsLayer = YES; self.layer.geometryFlipped = YES; - self.layer.masksToBounds = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; } if (@available(macOS 12.0, *)) { SquirrelTextLayoutManager *textLayoutManager = [[SquirrelTextLayoutManager alloc] init]; textLayoutManager.usesFontLeading = NO; + textLayoutManager.usesHyphenation = NO; textLayoutManager.delegate = textLayoutManager; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; @@ -807,7 +1035,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; [contentStorage addTextLayoutManager:textLayoutManager]; _textView = [[NSTextView alloc] initWithFrame:frameRect - textContainer:textLayoutManager.textContainer]; + textContainer:textContainer]; _textStorage = _textView.textContentStorage.textStorage; } else { SquirrelLayoutManager *layoutManager = [[SquirrelLayoutManager alloc] init]; @@ -840,11 +1068,27 @@ - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(maco return nil; } else { NSTextContentStorage *contentStorage = _textView.textContentStorage; - id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location - withOffset:(NSInteger)charRange.location]; - id endLocation = [contentStorage locationFromLocation:startLocation - withOffset:(NSInteger)charRange.length]; - return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; + id startLocation = + [contentStorage locationFromLocation:contentStorage.documentRange.location + withOffset:(NSInteger)charRange.location]; + id endLocation = + [contentStorage locationFromLocation:startLocation + withOffset:(NSInteger)charRange.length]; + return [[NSTextRange alloc] initWithLocation:startLocation + endLocation:endLocation]; + } +} + +- (NSRange)getCharRangeFromTextRange:(NSTextRange *)textRange API_AVAILABLE(macos(12.0)) { + if (textRange == nil) { + return NSMakeRange(NSNotFound, 0); + } else { + NSTextContentStorage *contentStorage = _textView.textContentStorage; + NSInteger location = [contentStorage offsetFromLocation:contentStorage.documentRange.location + toLocation:textRange.location]; + NSInteger length = [contentStorage offsetFromLocation:textRange.location + toLocation:textRange.endLocation]; + return NSMakeRange((NSUInteger)location, (NSUInteger)length); } } @@ -852,7 +1096,7 @@ - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(maco - (NSRect)contentRect { if (@available(macOS 12.0, *)) { [_textView.textLayoutManager ensureLayoutForRange:_textView.textContentStorage.documentRange]; - return [_textView.textLayoutManager usageBoundsForTextContainer]; + return _textView.textLayoutManager.usageBoundsForTextContainer; } else { [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; return [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; @@ -863,12 +1107,13 @@ - (NSRect)contentRect { - (NSRect)contentRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromCharRange:range]; - __block NSRect contentRect = NSZeroRect; + NSRect __block contentRect = NSZeroRect; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard + type:NSTextLayoutManagerSegmentTypeHighlight options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, + NSTextContainer *textContainer) { contentRect = NSUnionRect(contentRect, segFrame); return YES; }]; @@ -901,23 +1146,39 @@ - (NSRect)contentRectForRange:(NSRange)range { } // Will triger - (void)updateLayer -- (void)drawViewWithInsets:(NSEdgeInsets)insets +- (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets candidateRanges:(NSArray *)candidateRanges highlightedIndex:(NSUInteger)highlightedIndex preeditRange:(NSRange)preeditRange highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange - pagingButton:(NSUInteger)pagingButton { - _insets = insets; + pagingRange:(NSRange)pagingRange { + _alignmentRectInsets = alignmentRectInsets; _candidateRanges = candidateRanges; _highlightedIndex = highlightedIndex; _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; - _pagingButton = pagingButton; + _deleteBackPath = preeditRange.length > 0 ? [NSBezierPath bezierPath] : nil; _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; - self.needsDisplay = YES; + _functionButton = kVoidSymbol; + // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round + [self setNeedsDisplayInRect:self.bounds]; + [self.textView setNeedsDisplayInRect:self.bounds]; +} + +- (void)highlightFunctionButton:(rimeIndex)functionButton { + _functionButton = functionButton; + if (_deleteBackPath && !_deleteBackPath.empty) { + [self setNeedsDisplayInRect:_deleteBackPath.bounds]; + [self.textView setNeedsDisplayInRect:_deleteBackPath.bounds]; + } + if (_pagingPaths.count > 0) { + [self setNeedsDisplayInRect:_pagingPaths[0].bounds]; + [self setNeedsDisplayInRect:_pagingPaths[1].bounds]; + [self.textView setNeedsDisplayInRect:_pagingPaths[0].bounds]; + [self.textView setNeedsDisplayInRect:_pagingPaths[1].bounds]; + } } // Bezier cubic curve, which has continuous roundness @@ -965,7 +1226,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)insets return path; } -static NSArray * rectVertex(NSRect rect) { +static inline NSArray * rectVertex(NSRect rect) { return @[@(rect.origin), @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), @@ -984,25 +1245,32 @@ - (void)multilineRectForRange:(NSRange)charRange trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromCharRange:charRange]; - CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:charRange.location effectiveRange:NULL] lineSpacing]; - NSMutableArray *lineRects = [[NSMutableArray alloc] initWithCapacity:2]; - NSMutableArray *lineRanges = [[NSMutableArray alloc] initWithCapacity:2]; + NSMutableArray *lineRects = [[NSMutableArray alloc] init]; + NSMutableArray *lineRanges = [[NSMutableArray alloc] init]; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsMiddleFragmentsExcluded - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeHighlight + options:NSTextLayoutManagerSegmentOptionsNone + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, + NSTextContainer *textContainer) { if (!nearEmptyRect(segFrame)) { - segFrame.size.height += lineSpacing; - [lineRects addObject:[NSValue valueWithRect:segFrame]]; - [lineRanges addObject:segRange]; + NSRect lastSegFrame = lineRects.count > 0 ? lineRects.lastObject.rectValue : NSZeroRect; + if (NSMinY(segFrame) < NSMaxY(lastSegFrame)) { + segFrame = NSUnionRect(segFrame, lastSegFrame); + lineRects[lineRects.count - 1] = [NSValue valueWithRect:segFrame]; + segRange = [segRange textRangeByFormingUnionWithTextRange:lineRanges.lastObject]; + lineRanges[lineRanges.count - 1] = segRange; + } else { + [lineRects addObject:[NSValue valueWithRect:segFrame]]; + [lineRanges addObject:segRange]; + } } return YES; }]; if (lineRects.count == 1) { *bodyRect = lineRects[0].rectValue; } else { - CGFloat containerWidth = _textView.textContainer.size.width; + CGFloat containerWidth = self.contentRect.size.width; NSRect leadingLineRect = lineRects.firstObject.rectValue; leadingLineRect.size.width = containerWidth - NSMinX(leadingLineRect); NSRect trailingLineRect = lineRects.lastObject.rectValue; @@ -1037,12 +1305,12 @@ - (void)multilineRectForRange:(NSRange)charRange effectiveRange:&leadingLineRange]; CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect); + CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) ? + [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect); *bodyRect = NSMakeRect(startX, NSMinY(leadingLineRect), endX - startX, NSHeight(leadingLineRect)); } else { - CGFloat containerWidth = _textView.textContainer.size.width; + CGFloat containerWidth = self.contentRect.size.width; NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 effectiveRange:&trailingLineRange]; @@ -1079,27 +1347,27 @@ - (void)multilineRectForRange:(NSRange)charRange // Based on the 3 boxes from multilineRectForRange, calculate the vertex of the polygon containing the text in range static NSArray * multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { - if (nearEmptyRect(bodyRect) && !nearEmptyRect(leadingRect) && nearEmptyRect(trailingRect)) { + if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && NSIsEmptyRect(trailingRect)) { return rectVertex(leadingRect); - } else if (nearEmptyRect(bodyRect) && nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect)) { + } else if (NSIsEmptyRect(bodyRect) && NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect)) { return rectVertex(trailingRect); - } else if (nearEmptyRect(leadingRect) && nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { + } else if (NSIsEmptyRect(leadingRect) && NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { return rectVertex(bodyRect); - } else if (nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { + } else if (NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { NSArray *leadingVertex = rectVertex(leadingRect); NSArray *bodyVertex = rectVertex(bodyRect); return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3]]; - } else if (nearEmptyRect(leadingRect) && !nearEmptyRect(bodyRect)) { + } else if (NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(bodyRect)) { NSArray *trailingVertex = rectVertex(trailingRect); NSArray *bodyVertex = rectVertex(bodyRect); return @[bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3]]; - } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && - nearEmptyRect(bodyRect) && NSMaxX(leadingRect) > NSMinX(trailingRect)) { + } else if (!NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && + NSIsEmptyRect(bodyRect) && NSMinX(leadingRect) <= NSMaxX(trailingRect)) { NSArray *leadingVertex = rectVertex(leadingRect); NSArray *trailingVertex = rectVertex(trailingRect); return @[leadingVertex[0], leadingVertex[1], trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3]]; - } else if (!nearEmptyRect(leadingRect) && !nearEmptyRect(trailingRect) && !nearEmptyRect(bodyRect)) { + } else if (!NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { NSArray *leadingVertex = rectVertex(leadingRect); NSArray *bodyVertex = rectVertex(bodyRect); NSArray *trailingVertex = rectVertex(trailingRect); @@ -1110,218 +1378,276 @@ - (void)multilineRectForRange:(NSRange)charRange } } +static inline NSColor * hooverColor(NSColor *color, SquirrelAppear appear) { + if (color == nil) { + return nil; + } + if (@available(macOS 10.14, *)) { + return [color colorWithSystemEffect:NSColorSystemEffectRollover]; + } else { + return appear == darkAppear ? [color highlightWithLevel:0.3] : [color shadowWithLevel:0.3]; + } +} + +static inline NSColor * disabledColor(NSColor *color, SquirrelAppear appear) { + if (color == nil) { + return nil; + } + if (@available(macOS 10.14, *)) { + return [color colorWithSystemEffect:NSColorSystemEffectDisabled]; + } else { + return appear == darkAppear ? [color shadowWithLevel:0.3] : [color highlightWithLevel:0.3]; + } +} + +- (CAShapeLayer *)getFunctionButtonLayer { + SquirrelTheme *theme = self.currentTheme; + NSColor *buttonColor; + NSBezierPath *buttonPath; + switch (_functionButton) { + case kPageUp: + buttonColor = hooverColor(theme.linear ? theme.highlightedStripColor + : theme.highlightedPreeditColor, self.appear); + buttonPath = _pagingPaths[0]; + break; + case kHome: + buttonColor = disabledColor(theme.linear ? theme.highlightedStripColor + : theme.highlightedPreeditColor, self.appear); + buttonPath = _pagingPaths[0]; + break; + case kPageDown: + buttonColor = hooverColor(theme.linear ? theme.highlightedStripColor + : theme.highlightedPreeditColor, self.appear); + buttonPath = _pagingPaths[1]; + break; + case kEnd: + buttonColor = disabledColor(theme.linear ? theme.highlightedStripColor + : theme.highlightedPreeditColor, self.appear); + buttonPath = _pagingPaths[1]; + break; + case kBackSpace: + buttonColor = hooverColor(theme.highlightedPreeditColor, self.appear); + buttonPath = _deleteBackPath; + break; + case kEscape: + buttonColor = disabledColor(theme.highlightedPreeditColor, self.appear); + buttonPath= _deleteBackPath; + break; + default: + return nil; + break; + } + if (!buttonPath.empty && buttonColor) { + CAShapeLayer *functionButtonLayer = [[CAShapeLayer alloc] init]; + functionButtonLayer.path = buttonPath.quartzPath; + functionButtonLayer.fillColor = buttonColor.CGColor; + return functionButtonLayer; + } + return nil; +} + // All draws happen here - (void)updateLayer { + NSBezierPath *panelPath; NSBezierPath *backgroundPath; NSBezierPath *borderPath; - NSBezierPath *textContainerPath; NSBezierPath *highlightedPath; NSBezierPath *highlightedPreeditPath; NSBezierPath *candidateBlockPath; - NSBezierPath *candidateHorzGridPath; - NSBezierPath *candidateVertGridPath; - NSBezierPath *pageUpPath; - NSBezierPath *pageDownPath; + NSBezierPath *gridPath; SquirrelTheme *theme = self.currentTheme; - NSRect backgroundRect = self.bounds; - NSRect textContainerRect = [self backingAlignedRect:NSInsetRect(backgroundRect, theme.edgeInset.width, theme.edgeInset.height) - options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; + NSRect panelRect = self.bounds; + NSRect backgroundRect = [self backingAlignedRect:NSInsetRect(panelRect, theme.edgeInset.width, theme.edgeInset.height) + options:NSAlignAllEdgesNearest]; NSRange visibleRange; if (@available(macOS 12.0, *)) { - visibleRange = NSMakeRange(0, _textStorage.length); + visibleRange = [self getCharRangeFromTextRange: + _textView.textLayoutManager.textViewportLayoutController.viewportRange]; } else { - NSRange containerGlyphRange = {NSNotFound, 0}; - [_textView.layoutManager textContainerForGlyphAtIndex:0 effectiveRange:&containerGlyphRange]; - visibleRange = [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange actualGlyphRange:NULL]; + NSRange containerGlyphRange = NSMakeRange(NSNotFound, 0); + [_textView.layoutManager textContainerForGlyphAtIndex:0 + effectiveRange:&containerGlyphRange]; + visibleRange = [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange + actualGlyphRange:NULL]; } NSRange preeditRange = NSIntersectionRange(_preeditRange, visibleRange); NSRange candidateBlockRange = NSIntersectionRange(NSUnionRange(_candidateRanges.firstObject.rangeValue, theme.linear && _pagingRange.length > 0 ? _pagingRange : _candidateRanges.lastObject.rangeValue), visibleRange); NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange); + // Draw preedit Rect NSRect preeditRect = NSZeroRect; - NSRect candidateBlockRect = NSZeroRect; - NSRect pagingLineRect = NSZeroRect; if (preeditRange.length > 0) { preeditRect = [self contentRectForRange:preeditRange]; + preeditRect.size.width = backgroundRect.size.width; + preeditRect.origin = backgroundRect.origin; if (candidateBlockRange.length > 0) { preeditRect.size.height += theme.preeditLinespace; } - } - if (candidateBlockRange.length > 0) { - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, -theme.linespace / 2); - if (preeditRange.length == 0) { - candidateBlockRect.origin = textContainerRect.origin; - } - } - if (pagingRange.length > 0) { - pagingLineRect = [self contentRectForRange:pagingRange]; - if (!theme.linear) { - pagingLineRect.origin.y -= theme.pagingParagraphStyle.paragraphSpacingBefore; - pagingLineRect.size.height += theme.pagingParagraphStyle.paragraphSpacingBefore; - } - } - - [NSBezierPath setDefaultLineWidth:0]; - // Draw preedit Rect - if (preeditRange.length > 0) { - preeditRect.size.width = textContainerRect.size.width; - preeditRect.origin = textContainerRect.origin; preeditRect = [self backingAlignedRect:preeditRect - options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; + options:NSAlignAllEdgesNearest]; + // Draw highlighted part of preedit text NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); - if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor != nil) { - NSRect innerBox = NSInsetRect(preeditRect, theme.separatorWidth / 2, 0); + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3); + if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor) { + CGFloat kerning = [theme.preeditAttrs[NSKernAttributeName] doubleValue]; + NSRect innerBox = NSMakeRect(preeditRect.origin.x + ceil(theme.separatorWidth * 0.5) - ceil(kerning * 0.5), preeditRect.origin.y, + preeditRect.size.width - theme.separatorWidth + kerning, preeditRect.size.height); if (candidateBlockRange.length > 0) { innerBox.size.height -= theme.preeditLinespace; } + innerBox = [self backingAlignedRect:innerBox options:NSAlignAllEdgesNearest]; NSRect leadingRect = NSZeroRect; NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(leadingRect, innerBox) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(bodyRect, innerBox) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : [self backingAlignedRect:NSIntersectionRect(trailingRect, innerBox) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - NSArray *highlightedPreeditPoints; - NSArray *highlightedPreeditPoints2; + [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect + bodyRect:&bodyRect trailingRect:&trailingRect]; + leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : + [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(leadingRect, - ceil(kerning * 0.5), 0.0), innerBox) + options:NSAlignAllEdgesNearest]; + bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : + [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(bodyRect, - ceil(kerning * 0.5), 0.0), innerBox) + options:NSAlignAllEdgesNearest]; + trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : + [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(trailingRect, - ceil(kerning * 0.5), 0.0), innerBox) + options:NSAlignAllEdgesNearest]; + // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPoints = rectVertex(leadingRect); - highlightedPreeditPoints2 = rectVertex(trailingRect); + highlightedPreeditPath = drawRoundedPolygon(rectVertex(leadingRect), cornerRadius); + [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(rectVertex(trailingRect), cornerRadius)]; } else { - highlightedPreeditPoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); - } - highlightedPreeditPath = drawRoundedPolygon(highlightedPreeditPoints, - MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3)); - if (highlightedPreeditPoints2) { - [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(highlightedPreeditPoints2, - MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3))]; + highlightedPreeditPath = drawRoundedPolygon(multilineRectVertex(leadingRect, bodyRect, trailingRect), cornerRadius); } } + NSRect deleteBackRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_preeditRange) - 1, 1)]; + deleteBackRect.size.width += floor(theme.separatorWidth * 0.5); + deleteBackRect.origin.x = NSMaxX(backgroundRect) - NSWidth(deleteBackRect); + deleteBackRect = [self backingAlignedRect:NSIntersectionRect(deleteBackRect, preeditRect) + options:NSAlignAllEdgesNearest]; + _deleteBackPath = drawRoundedPolygon(rectVertex(deleteBackRect), cornerRadius); } // Draw candidate Rect + NSRect candidateBlockRect = NSZeroRect; if (candidateBlockRange.length > 0) { - candidateBlockRect.size.width = textContainerRect.size.width; - candidateBlockRect.origin.x = textContainerRect.origin.x; - candidateBlockRect = [self backingAlignedRect:NSIntersectionRect(candidateBlockRect, textContainerRect) - options:NSAlignMinXOutward|NSAlignMinYOutward|NSAlignWidthNearest|NSAlignHeightNearest|NSAlignRectFlipped]; - candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), theme.highlightedCornerRadius); + candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, - theme.linespace * 0.5); + candidateBlockRect.size.width = backgroundRect.size.width; + candidateBlockRect.origin = backgroundRect.origin; + if (preeditRange.length != 0) { + candidateBlockRect.origin.y = NSMaxY(preeditRect); + } + if (pagingRange.length == 0 || theme.linear) { + candidateBlockRect.size.height = NSMaxY(backgroundRect) - NSMinY(candidateBlockRect); + } + candidateBlockRect = [self backingAlignedRect:NSIntersectionRect(candidateBlockRect, backgroundRect) + options:NSAlignAllEdgesNearest]; + candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), + MIN(theme.highlightedCornerRadius, NSHeight(candidateBlockRect) / 3)); // Draw candidate highlight rect + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, theme.paragraphStyle.maximumLineHeight / 3); if (theme.linear) { CGFloat gridOriginY = NSMinY(candidateBlockRect); CGFloat tabInterval = theme.separatorWidth * 2; - if (theme.tabled) { - candidateHorzGridPath = [NSBezierPath bezierPath]; - candidateVertGridPath = [NSBezierPath bezierPath]; - } + CGFloat kerning = [theme.attrs[NSKernAttributeName] doubleValue]; for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + NSRange candidateRange = NSIntersectionRange(_candidateRanges[i].rangeValue, visibleRange); if (candidateRange.length == 0) { break; } NSRect leadingRect = NSZeroRect; NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:candidateRange leadingRect:&leadingRect bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : NSInsetRect(leadingRect, -theme.separatorWidth / 2, 0); - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : NSInsetRect(bodyRect, -theme.separatorWidth / 2, 0); - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : NSInsetRect(trailingRect, -theme.separatorWidth / 2, 0); - if (!NSIsEmptyRect(leadingRect)) { - leadingRect.origin.y -= theme.linespace / 2; - leadingRect.size.height += theme.linespace / 2; + [self multilineRectForRange:candidateRange leadingRect:&leadingRect + bodyRect:&bodyRect trailingRect:&trailingRect]; + if (nearEmptyRect(leadingRect)) { + leadingRect = NSZeroRect; + bodyRect.origin.y -= ceil(theme.linespace * 0.5); + bodyRect.size.height += ceil(theme.linespace * 0.5); + } else { + leadingRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); + leadingRect.size.width += theme.separatorWidth + kerning * 0.5; + leadingRect.origin.y -= ceil(theme.linespace * 0.5); + leadingRect.size.height += ceil(theme.linespace * 0.5); leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + options:NSAlignAllEdgesNearest]; } - if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.height += theme.linespace / 2; - trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + if (nearEmptyRect(trailingRect)) { + trailingRect = NSZeroRect; + bodyRect.size.height += floor(theme.linespace * 0.5); + } else { + trailingRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); + trailingRect.size.width += theme.separatorWidth + kerning * 0.5; + trailingRect.size.height += floor(theme.linespace * 0.5); + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) + options:NSAlignAllEdgesNearest]; } - if (!NSIsEmptyRect(bodyRect)) { - if (NSIsEmptyRect(leadingRect)) { - bodyRect.origin.y -= theme.linespace / 2; - bodyRect.size.height += theme.linespace / 2; - } - if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.height += theme.linespace / 2; - } + if (nearEmptyRect(bodyRect)) { + bodyRect = NSZeroRect; + } else { + bodyRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); + bodyRect.size.width += theme.separatorWidth + kerning * 0.5; bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + options:NSAlignAllEdgesNearest]; } if (theme.tabled) { CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border - [candidateHorzGridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth / 2, bottomEdge)]; - [candidateHorzGridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth / 2, bottomEdge)]; - [candidateHorzGridPath closePath]; + [gridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth * 0.5, bottomEdge)]; + [gridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth * 0.5, bottomEdge)]; + [gridPath closePath]; gridOriginY = bottomEdge; } CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; - if (leadOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth / 2) { // vertical bar - [candidateVertGridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius / 2)]; - [candidateVertGridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + theme.linespace / 2 + theme.highlightedCornerRadius / 2)]; - [candidateVertGridPath closePath]; + if (leadOrigin.x > NSMinX(candidateBlockRect) +theme.separatorWidth * 0.5) { // vertical bar + [gridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + ceil(theme.linespace * 0.5) + + theme.highlightedCornerRadius / 2)]; + [gridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + floor(theme.linespace * 0.5) + + theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius / 2)]; + [gridPath closePath]; } CGFloat endEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((endEdge - textContainerRect.origin.x) / tabInterval / 2) * tabInterval * 2 + textContainerRect.origin.x; - if (i == _candidateRanges.count - 1 && pagingRange.length > 0 && - bottomEdge > NSMinY(pagingLineRect) && tabPosition > NSMinX(pagingLineRect)) { - tabPosition -= tabInterval; - } - if (NSIsEmptyRect(trailingRect)) { - bodyRect.size.width += tabPosition - endEdge; - bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - } else if (NSIsEmptyRect(bodyRect)) { + CGFloat tabPosition = ceil((endEdge + kerning + theme.separatorWidth * 0.5) / tabInterval) * tabInterval - theme.separatorWidth * 0.5; + if (!NSIsEmptyRect(trailingRect)) { trailingRect.size.width += tabPosition - endEdge; trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - } else { - bodyRect = NSMakeRect(NSMinX(candidateBlockRect), NSMinY(bodyRect), - NSWidth(candidateBlockRect), NSHeight(bodyRect) + NSHeight(trailingRect)); - trailingRect = NSZeroRect; + options:NSAlignAllEdgesNearest]; + } else if (NSIsEmptyRect(leadingRect)) { + bodyRect.size.width += tabPosition - endEdge; bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; + options:NSAlignAllEdgesNearest]; } } - NSArray *candidatePoints; - NSArray *candidatePoints2; + NSBezierPath *candidatePath; // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePoints = rectVertex(leadingRect); - candidatePoints2 = rectVertex(trailingRect); + candidatePath = drawRoundedPolygon(rectVertex(leadingRect), cornerRadius); + [candidatePath appendBezierPath:drawRoundedPolygon(rectVertex(trailingRect), cornerRadius)]; } else { - candidatePoints = multilineRectVertex(leadingRect, bodyRect, trailingRect); - } - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); - if (candidatePoints2) { - [candidatePath appendBezierPath:drawRoundedPolygon(candidatePoints2, theme.highlightedCornerRadius)]; + candidatePath = drawRoundedPolygon(multilineRectVertex(leadingRect, bodyRect, trailingRect), cornerRadius); } _candidatePaths[i] = candidatePath; } } else { // stacked layout for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { - NSRange candidateRange = NSIntersectionRange([_candidateRanges[i] rangeValue], visibleRange); + NSRange candidateRange = NSIntersectionRange(_candidateRanges[i].rangeValue, visibleRange); if (candidateRange.length == 0) { break; } - NSRect candidateRect = NSInsetRect([self contentRectForRange:candidateRange], 0.0, -theme.linespace / 2); - candidateRect.size.width = textContainerRect.size.width; - candidateRect.origin.x = textContainerRect.origin.x; + NSRect candidateRect = [self contentRectForRange:candidateRange]; + candidateRect.size.width = backgroundRect.size.width; + candidateRect.origin.x = backgroundRect.origin.x; + candidateRect.origin.y -= ceil(theme.linespace * 0.5); + candidateRect.size.height += theme.linespace; candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - NSArray *candidatePoints = rectVertex(candidateRect); - NSBezierPath *candidatePath = drawRoundedPolygon(candidatePoints, theme.highlightedCornerRadius); + options:NSAlignAllEdgesNearest]; + NSBezierPath *candidatePath = drawRoundedPolygon(rectVertex(candidateRect), cornerRadius); _candidatePaths[i] = candidatePath; } } @@ -1329,173 +1655,134 @@ - (void)updateLayer { // Draw paging Rect if (pagingRange.length > 0) { - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; - pageDownRect.size.width += theme.separatorWidth / 2; NSRect pageUpRect = [self contentRectForRange:NSMakeRange(pagingRange.location, 1)]; - pageUpRect.origin.x -= theme.separatorWidth / 2; + NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; + pageDownRect.size.width += ceil(theme.separatorWidth * 0.5); + pageUpRect.origin.x -= ceil(theme.separatorWidth * 0.5); pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented if (theme.linear) { - pageDownRect = NSInsetRect(pageDownRect, 0.0, -theme.linespace / 2); - pageUpRect = NSInsetRect(pageUpRect, 0.0, -theme.linespace / 2); + pageUpRect.origin.y -= ceil(theme.linespace * 0.5); + pageUpRect.size.height += theme.linespace; + pageDownRect.origin.y -= ceil(theme.linespace * 0.5); + pageDownRect.size.height += theme.linespace; + pageUpRect = NSIntersectionRect(pageUpRect, candidateBlockRect); + pageDownRect = NSIntersectionRect(pageDownRect, candidateBlockRect); + } else { + NSRect pagingRect = NSMakeRect(NSMinX(backgroundRect), NSMaxY(candidateBlockRect), + NSWidth(backgroundRect), NSMaxY(backgroundRect) - NSMaxY(candidateBlockRect)); + pageUpRect = NSIntersectionRect(pageUpRect, pagingRect); + pageDownRect = NSIntersectionRect(pageDownRect, pagingRect); } - pageDownRect = [self backingAlignedRect:NSIntersectionRect(pageDownRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - pageUpRect = [self backingAlignedRect:NSIntersectionRect(pageUpRect, candidateBlockRect) - options:NSAlignAllEdgesOutward|NSAlignRectFlipped]; - pageDownPath = drawRoundedPolygon(rectVertex(pageDownRect), - MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3)); - pageUpPath = drawRoundedPolygon(rectVertex(pageUpRect), - MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageUpRect), NSHeight(pageUpRect)) / 3)); - _pagingPaths[0] = pageUpPath; - _pagingPaths[1] = pageDownPath; + pageUpRect = [self backingAlignedRect:pageUpRect options:NSAlignAllEdgesNearest]; + pageDownRect = [self backingAlignedRect:pageDownRect options:NSAlignAllEdgesNearest]; + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3); + _pagingPaths[0] = drawRoundedPolygon(rectVertex(pageUpRect), cornerRadius); + _pagingPaths[1] = drawRoundedPolygon(rectVertex(pageDownRect), cornerRadius); } // Draw borders backgroundPath = drawRoundedPolygon(rectVertex(backgroundRect), - MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3)); - textContainerPath = drawRoundedPolygon(rectVertex(textContainerRect), - MIN(theme.highlightedCornerRadius, NSHeight(textContainerRect) / 3)); - if (theme.edgeInset.width > 0 || theme.edgeInset.height > 0) { - borderPath = [backgroundPath copy]; - [borderPath appendBezierPath:textContainerPath]; - borderPath.windingRule = NSWindingRuleEvenOdd; + MIN(theme.highlightedCornerRadius, NSHeight(backgroundRect) / 3)); + panelPath = drawRoundedPolygon(rectVertex(panelRect), MIN(theme.cornerRadius, NSHeight(panelRect) / 3)); + if (!NSEqualSizes(theme.edgeInset, NSZeroSize)) { + borderPath = [panelPath copy]; + [borderPath appendBezierPath:backgroundPath]; } // Set layers - _shape.path = [backgroundPath quartzPath]; - _shape.fillColor = [[NSColor whiteColor] CGColor]; - _shape.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); - CAShapeLayer *textContainerLayer = [[CAShapeLayer alloc] init]; - textContainerLayer.path = [textContainerPath quartzPath]; - textContainerLayer.fillColor = [[NSColor whiteColor] CGColor]; - textContainerLayer.cornerRadius = MIN(theme.highlightedCornerRadius, NSHeight(textContainerRect) / 3); + _shape.path = panelPath.quartzPath; + _shape.fillColor = NSColor.whiteColor.CGColor; [self.layer setSublayers:nil]; - self.layer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); CALayer *panelLayer = [[CALayer alloc] init]; + if (theme.translucency > 0) { + panelLayer.opacity = 1.0f - (float)theme.translucency; + } [self.layer addSublayer:panelLayer]; - if (theme.backgroundImage) { + // background image (pattern style) layer + if (theme.backgroundImage && theme.backgroundImage.valid) { CAShapeLayer *backgroundImageLayer = [[CAShapeLayer alloc] init]; - if (theme.vertical) { - const CGAffineTransform rotate = CGAffineTransformMakeRotation(-M_PI_2); - backgroundImageLayer.path = CFAutorelease(CGPathCreateCopyByTransformingPath([textContainerPath quartzPath], &rotate)); - backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; - [backgroundImageLayer setAffineTransform:CGAffineTransformInvert(rotate)]; - } else { - backgroundImageLayer.path = [textContainerPath quartzPath]; - backgroundImageLayer.fillColor = [theme.backgroundImage CGColor]; - } - backgroundImageLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); + CGAffineTransform transform = theme.vertical ? CGAffineTransformMakeRotation(M_PI_2) : CGAffineTransformIdentity; + transform = CGAffineTransformTranslate(transform, _alignmentRectInsets.left - theme.edgeInset.width, + _alignmentRectInsets.top - theme.edgeInset.height); + backgroundImageLayer.path = CFAutorelease(CGPathCreateCopyByTransformingPath(backgroundPath.quartzPath, &transform)); + backgroundImageLayer.fillColor = [NSColor colorWithPatternImage:theme.backgroundImage].CGColor; + backgroundImageLayer.affineTransform = CGAffineTransformInvert(transform); [panelLayer addSublayer:backgroundImageLayer]; } + // background color layer CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; - backgroundLayer.path = [textContainerPath quartzPath]; - backgroundLayer.fillColor = [theme.backgroundColor CGColor]; - backgroundLayer.cornerRadius = MIN(theme.cornerRadius, NSHeight(backgroundRect) / 3); + backgroundLayer.path = backgroundPath.quartzPath; + backgroundLayer.fillColor = theme.backgroundColor.CGColor; [panelLayer addSublayer:backgroundLayer]; - if (theme.preeditBackgroundColor && - (preeditRange.length > 0 || !NSIsEmptyRect(pagingLineRect))) { - backgroundLayer.fillColor = [theme.preeditBackgroundColor CGColor]; + if ((_preeditRange.length > 0 || (!theme.linear && _pagingRange.length > 0)) && + theme.preeditBackgroundColor) { + backgroundLayer.fillColor = theme.preeditBackgroundColor.CGColor; if (!candidateBlockPath.empty) { - [textContainerPath appendBezierPath:candidateBlockPath]; - textContainerPath.windingRule = NSWindingRuleEvenOdd; - backgroundLayer.path = [textContainerPath quartzPath]; + [backgroundPath appendBezierPath:candidateBlockPath.bezierPathByReversingPath]; + backgroundLayer.path = backgroundPath.quartzPath; backgroundLayer.fillRule = kCAFillRuleEvenOdd; CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; - candidateLayer.path = [candidateBlockPath quartzPath]; - candidateLayer.fillColor = [theme.backgroundColor CGColor]; - candidateLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; - candidateLayer.shadowOffset = NSMakeSize(theme.preeditLinespace / 2, - theme.preeditLinespace / 2); + candidateLayer.path = candidateBlockPath.quartzPath; + candidateLayer.fillColor = theme.backgroundColor.CGColor; [panelLayer addSublayer:candidateLayer]; } } - if (theme.translucency > 0) { - panelLayer.opacity = (float)(1.0 - theme.translucency); + // highlighted preedit layer + if (theme.highlightedPreeditColor && !highlightedPreeditPath.empty) { + CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; + highlightedPreeditLayer.path = highlightedPreeditPath.quartzPath; + highlightedPreeditLayer.fillColor = theme.highlightedPreeditColor.CGColor; + [self.layer addSublayer:highlightedPreeditLayer]; } + // highlighted candidate layer if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; highlightedPath = _candidatePaths[_highlightedIndex]; - highlightedLayer.path = [highlightedPath quartzPath]; - highlightedLayer.fillColor = [theme.highlightedStripColor CGColor]; - highlightedLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; - highlightedLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); - CAShapeLayer *candidateMaskLayer = [[CAShapeLayer alloc] init]; - candidateMaskLayer.path = [candidateBlockPath quartzPath]; - candidateMaskLayer.fillColor = [[NSColor whiteColor] CGColor]; - highlightedLayer.mask = candidateMaskLayer; + highlightedLayer.path = highlightedPath.quartzPath; + highlightedLayer.fillColor = theme.highlightedStripColor.CGColor; [self.layer addSublayer:highlightedLayer]; } - NSColor *buttonColor = theme.linear ? theme.highlightedStripColor : theme.highlightedPreeditColor; - if (pagingRange.length > 0 && buttonColor) { - CAShapeLayer *pagingLayer = [[CAShapeLayer alloc] init]; - switch (_pagingButton) { - case NSPageUpFunctionKey: - pagingLayer.path = [pageUpPath quartzPath]; - pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; - break; - case NSBeginFunctionKey: - pagingLayer.path = [pageUpPath quartzPath]; - pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; - break; - case NSPageDownFunctionKey: - pagingLayer.path = [pageDownPath quartzPath]; - pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectRollover] CGColor]; - break; - case NSEndFunctionKey: - pagingLayer.path = [pageDownPath quartzPath]; - pagingLayer.fillColor = [[buttonColor colorWithSystemEffect:NSColorSystemEffectDisabled] CGColor]; - break; - } - pagingLayer.mask = textContainerLayer; - pagingLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; - pagingLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); - [self.layer addSublayer:pagingLayer]; - } - if (theme.highlightedPreeditColor) { - if (!highlightedPreeditPath.empty) { - CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; - highlightedPreeditLayer.path = [highlightedPreeditPath quartzPath]; - highlightedPreeditLayer.fillColor = [theme.highlightedPreeditColor CGColor]; - highlightedPreeditLayer.mask = textContainerLayer; - highlightedPreeditLayer.shadowOpacity = [theme.backgroundColor brightnessComponent]; - highlightedPreeditLayer.shadowOffset = NSMakeSize(theme.highlightedCornerRadius / 2, - theme.highlightedCornerRadius / 2); - [self.layer addSublayer:highlightedPreeditLayer]; + // function buttons (page up, page down, backspace) layer + if (_functionButton != kVoidSymbol) { + CAShapeLayer *functionButtonLayer = [self getFunctionButtonLayer]; + if (functionButtonLayer) { + [self.layer addSublayer:functionButtonLayer]; } } + // grids (in candidate block) layer if (theme.tabled) { - CAShapeLayer *horzGridLayer = [[CAShapeLayer alloc] init]; - horzGridLayer.path = [candidateHorzGridPath quartzPath]; - horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction - ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; - horzGridLayer.lineWidth = theme.edgeInset.height / 2; - horzGridLayer.lineCap = kCALineCapRound; - [panelLayer addSublayer:horzGridLayer]; - CAShapeLayer *vertGridLayer = [[CAShapeLayer alloc] init]; - vertGridLayer.path = [candidateVertGridPath quartzPath]; - vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction - ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; - vertGridLayer.lineWidth = theme.edgeInset.width / 2; - vertGridLayer.lineCap = kCALineCapRound; - [panelLayer addSublayer:vertGridLayer]; - } - if (theme.borderColor && !borderPath.empty) { + CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; + gridLayer.path = gridPath.quartzPath; + gridLayer.strokeColor = [theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:(self.appear == darkAppear ? NSColor.lightGrayColor : NSColor.blackColor)].CGColor; + gridLayer.lineWidth = theme.edgeInset.height * 0.5; + gridLayer.lineCap = kCALineCapRound; + [panelLayer addSublayer:gridLayer]; + } + // border layer + if (!borderPath.empty && theme.borderColor) { CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; - borderLayer.path = [borderPath quartzPath]; - borderLayer.fillColor = [theme.borderColor CGColor]; + borderLayer.path = borderPath.quartzPath; + borderLayer.fillColor = theme.borderColor.CGColor; borderLayer.fillRule = kCAFillRuleEvenOdd; [panelLayer addSublayer:borderLayer]; } - [_textView display]; } -- (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { +- (BOOL)convertClickSpot:(NSPoint)spot + toIndex:(NSUInteger *)index { if (NSPointInRect(spot, self.bounds)) { + if (_deleteBackPath != nil && [_deleteBackPath containsPoint:spot]) { + *index = kBackSpace; + return YES; + } if (_pagingPaths.count > 0) { if ([_pagingPaths[0] containsPoint:spot]) { - *index = NSPageUpFunctionKey; // borrow function-key unicode for readability + *index = kPageUp; return YES; } if ([_pagingPaths[1] containsPoint:spot]) { - *index = NSPageDownFunctionKey; // borrow function-key unicode for readability + *index = kPageDown; return YES; } } @@ -1512,22 +1799,25 @@ - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { @end // SquirrelView +#pragma mark - Panel window, dealing with text content and mouse interactions + + @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; NSSize _maxSize; CGFloat _textWidthLimit; + NSPoint _scrollLocus; + BOOL _initPosition; NSUInteger _numCandidates; - NSUInteger _index; - NSUInteger _turnPage; + NSUInteger _highlighted; + NSUInteger _functionButton; + BOOL _caretAtHome; BOOL _firstPage; BOOL _lastPage; - NSPoint _scrollLocus; - BOOL _initPosition; - NSString *_statusMessage; NSTimer *_statusTimer; } @@ -1567,70 +1857,54 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { // prevent mac terminal from hijacking non-alphabetic keys on non-inline mode defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); - NSMutableDictionary *attrs = [defaultAttrs mutableCopy]; - attrs[NSForegroundColorAttributeName] = [NSColor controlTextColor]; + NSMutableDictionary *attrs = defaultAttrs.mutableCopy; + attrs[NSForegroundColorAttributeName] = NSColor.controlTextColor; attrs[NSFontAttributeName] = userFont; - NSMutableDictionary *highlightedAttrs = [defaultAttrs mutableCopy]; - highlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedMenuItemTextColor]; + NSMutableDictionary *highlightedAttrs = defaultAttrs.mutableCopy; + highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; highlightedAttrs[NSFontAttributeName] = userFont; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. attrs[NSWritingDirectionAttributeName] = @[@(0)]; highlightedAttrs[NSWritingDirectionAttributeName] = @[@(0)]; - NSMutableDictionary *labelAttrs = [attrs mutableCopy]; - labelAttrs[NSForegroundColorAttributeName] = [NSColor controlAccentColor]; + NSMutableDictionary *labelAttrs = attrs.mutableCopy; + labelAttrs[NSForegroundColorAttributeName] = NSColor.controlAccentColor; labelAttrs[NSFontAttributeName] = userMonoFont; - NSMutableDictionary *labelHighlightedAttrs = [highlightedAttrs mutableCopy]; - labelHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + NSMutableDictionary *labelHighlightedAttrs = highlightedAttrs.mutableCopy; + labelHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; labelHighlightedAttrs[NSFontAttributeName] = userMonoFont; - NSMutableDictionary *commentAttrs = [defaultAttrs mutableCopy]; - commentAttrs[NSForegroundColorAttributeName] = [NSColor secondaryLabelColor]; + NSMutableDictionary *commentAttrs = defaultAttrs.mutableCopy; + commentAttrs[NSForegroundColorAttributeName] = NSColor.secondaryLabelColor; commentAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *commentHighlightedAttrs = [defaultAttrs mutableCopy]; - commentHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor alternateSelectedControlTextColor]; + NSMutableDictionary *commentHighlightedAttrs = defaultAttrs.mutableCopy; + commentHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; commentHighlightedAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *preeditAttrs = [defaultAttrs mutableCopy]; - preeditAttrs[NSForegroundColorAttributeName] = [NSColor textColor]; + NSMutableDictionary *preeditAttrs = defaultAttrs.mutableCopy; + preeditAttrs[NSForegroundColorAttributeName] = NSColor.textColor; preeditAttrs[NSFontAttributeName] = userFont; preeditAttrs[NSLigatureAttributeName] = @(0); - NSMutableDictionary *preeditHighlightedAttrs = [defaultAttrs mutableCopy]; - preeditHighlightedAttrs[NSForegroundColorAttributeName] = [NSColor selectedTextColor]; + NSMutableDictionary *preeditHighlightedAttrs = defaultAttrs.mutableCopy; + preeditHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedTextColor; preeditHighlightedAttrs[NSFontAttributeName] = userFont; preeditHighlightedAttrs[NSLigatureAttributeName] = @(0); - NSMutableDictionary *pagingAttrs = [defaultAttrs mutableCopy]; - pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? [NSColor controlAccentColor] : [NSColor controlTextColor]; + NSMutableDictionary *pagingAttrs = defaultAttrs.mutableCopy; + pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? NSColor.controlAccentColor : NSColor.controlTextColor; - NSMutableDictionary *pagingHighlightedAttrs = [defaultAttrs mutableCopy]; + NSMutableDictionary *pagingHighlightedAttrs = defaultAttrs.mutableCopy; pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear - ? [NSColor alternateSelectedControlTextColor] : [NSColor selectedMenuItemTextColor]; - - if (@available(macOS 12.0, *)) { - attrs[NSTrackingAttributeName] = @(1); - highlightedAttrs[NSTrackingAttributeName] = @(1); - commentAttrs[NSTrackingAttributeName] = @(1); - commentHighlightedAttrs[NSTrackingAttributeName] = @(1); - preeditAttrs[NSTrackingAttributeName] = @(1); - preeditHighlightedAttrs[NSTrackingAttributeName] = @(1); - } else { - attrs[NSKernAttributeName] = @(1); - highlightedAttrs[NSKernAttributeName] = @(1); - commentAttrs[NSKernAttributeName] = @(1); - commentHighlightedAttrs[NSKernAttributeName] = @(1); - preeditAttrs[NSKernAttributeName] = @(1); - preeditHighlightedAttrs[NSKernAttributeName] = @(1); - } + ? NSColor.alternateSelectedControlTextColor : NSColor.selectedMenuItemTextColor; - NSMutableParagraphStyle *preeditParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - NSMutableParagraphStyle *pagingParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; - NSMutableParagraphStyle *statusParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + NSMutableParagraphStyle *preeditParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; + NSMutableParagraphStyle *paragraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; + NSMutableParagraphStyle *pagingParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; + NSMutableParagraphStyle *statusParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; preeditParagraphStyle.alignment = NSTextAlignmentLeft; @@ -1645,7 +1919,7 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - NSMutableDictionary *statusAttrs = [commentAttrs mutableCopy]; + NSMutableDictionary *statusAttrs = commentAttrs.mutableCopy; statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; @@ -1674,16 +1948,18 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { - (instancetype)init { self = [super initWithContentRect:_position - styleMask:NSWindowStyleMaskNonactivatingPanel | NSWindowStyleMaskBorderless | NSWindowStyleMaskHUDWindow | NSWindowStyleMaskUtilityWindow + styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; - if (self) { + self.level = CGWindowLevelForKey(kCGCursorWindowLevelKey) - 10; self.alphaValue = 1.0; self.hasShadow = NO; self.opaque = NO; self.displaysWhenScreenProfileChanges = YES; - self.backgroundColor = [NSColor clearColor]; + self.backgroundColor = NSColor.clearColor; + self.delegate = self; + NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.bounds]; _back = [[NSVisualEffectView alloc] init]; @@ -1695,16 +1971,33 @@ - (instancetype)init { [contentView addSubview:_back]; [contentView addSubview:_view]; [contentView addSubview:_view.textView]; - self.contentView = contentView; + + [self updateDisplayParameters]; [self initializeUIStyleForAppearance:defaultAppear]; [self initializeUIStyleForAppearance:darkAppear]; - _maxSize = NSZeroSize; - _initPosition = YES; } return self; } +- (void)windowDidChangeScreenProfile:(NSNotification *)notification { + if (self.isVisible) { + [self.inputController.client attributesForCharacterIndex:0 + lineHeightRectangle:&_position]; + [self updateDisplayParameters]; + [self display]; + } +} + +- (void)windowDidChangeScreen:(NSNotification *)notification { + if (self.isVisible) { + [self.inputController.client attributesForCharacterIndex:0 + lineHeightRectangle:&_position]; + [self updateDisplayParameters]; + [self display]; + } +} + - (void)sendEvent:(NSEvent *)event { NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream fromView:nil]; @@ -1712,48 +2005,76 @@ - (void)sendEvent:(NSEvent *)event { switch (event.type) { case NSEventTypeLeftMouseUp: if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index) { - [_inputController perform:kSELECT onIndex:(rimeIndex)cursorIndex]; - } else if (cursorIndex == _turnPage) { - rimeIndex indexChar = cursorIndex == NSPageUpFunctionKey ? kPageUp : - (cursorIndex == NSPageDownFunctionKey ? kPageDown : kVoidSymbol); - [_inputController perform:kSELECT onIndex:indexChar]; + if (cursorIndex == _highlighted || cursorIndex == _functionButton) { + [_inputController perform:kSELECT onIndex:cursorIndex]; } } break; case NSEventTypeRightMouseUp: if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index) { - [_inputController perform:kDELETE onIndex:(rimeIndex)cursorIndex]; - } else if (cursorIndex == _turnPage) { - [_inputController perform:kSELECT onIndex:kEscape]; + if (cursorIndex == _highlighted) { + [_inputController perform:kDELETE onIndex:cursorIndex]; + } else if (cursorIndex == _functionButton) { + switch (cursorIndex) { + case kPageUp: + [_inputController perform:kSELECT onIndex:kHome]; + break; + case kPageDown: + [_inputController perform:kSELECT onIndex:kEnd]; + break; + case kBackSpace: + [_inputController perform:kSELECT onIndex:kEscape]; + break; + } } } break; case NSEventTypeMouseMoved: if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex >= 0 && cursorIndex < _numCandidates && _index != cursorIndex) { - _index = cursorIndex; - [_inputController perform:kHILITE onIndex:(rimeIndex)cursorIndex]; - } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { - _turnPage = cursorIndex; - if (_turnPage == NSPageUpFunctionKey) { - [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; - [_view.textStorage addAttributes:_view.currentTheme.pagingAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - cursorIndex = _firstPage ? NSBeginFunctionKey : NSPageUpFunctionKey; - } else if (_turnPage == NSPageDownFunctionKey) { - [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - [_view.textStorage addAttributes:_view.currentTheme.pagingAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; - cursorIndex = _lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlighted != cursorIndex) { + _highlighted = cursorIndex; + [_inputController perform:kHILITE onIndex:[_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; + } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || + cursorIndex == kBackSpace) && _functionButton != cursorIndex) { + _functionButton = cursorIndex; + SquirrelTheme *theme = _view.currentTheme; + switch (_functionButton) { + case kPageUp: + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _firstPage ? kHome : kPageUp; + break; + case kPageDown: + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _lastPage ? kEnd : kPageDown; + break; + case kBackSpace: + [_view.textStorage addAttributes:theme.preeditHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + if (_view.pagingRange.length > 0) { + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + } + cursorIndex = _caretAtHome ? kEscape : kBackSpace; + break; } - [_view drawViewWithInsets:_view.insets - candidateRanges:_view.candidateRanges - highlightedIndex:_view.highlightedIndex - preeditRange:_view.preeditRange - highlightedPreeditRange:_view.highlightedPreeditRange - pagingRange:_view.pagingRange - pagingButton:cursorIndex]; - [self show]; + [_view highlightFunctionButton:cursorIndex]; + [self display]; } } break; @@ -1777,16 +2098,20 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageDown : kPageUp)]; + [_inputController perform:kSELECT + onIndex:(theme.vertical ? kPageDown : kPageUp)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { - [_inputController perform:kSELECT onIndex:kPageUp]; + [_inputController perform:kSELECT + onIndex:kPageUp]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageUp : kPageDown)]; + [_inputController perform:kSELECT + onIndex:(theme.vertical ? kPageUp : kPageDown)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:kPageDown]; + [_inputController perform:kSELECT + onIndex:kPageDown]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } } @@ -1797,25 +2122,53 @@ - (void)sendEvent:(NSEvent *)event { } } -- (void)getTextWidthLimit { - NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; +- (NSScreen *)screen { + if (!NSIsEmptyRect(_position)) { + for (NSScreen *screen in NSScreen.screens) { + if (NSPointInRect(_position.origin, screen.frame)) { + return screen; + } + } + } + return NSScreen.mainScreen; +} + +- (void)updateDisplayParameters { + // repositioning the panel window + _initPosition = YES; + _maxSize = NSZeroSize; + + // size limits on textContainer + NSRect screenRect = self.screen.visibleFrame; SquirrelTheme *theme = _view.currentTheme; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - theme.separatorWidth - theme.edgeInset.width * 2; + CGFloat textWidthRatio = MIN(0.5, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - theme.separatorWidth - theme.edgeInset.width * 2 -kOffsetGap; if (theme.lineLength > 0) { _textWidthLimit = MIN(theme.lineLength, _textWidthLimit); } if (theme.tabled) { CGFloat tabInterval = theme.separatorWidth * 2; - _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; + _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; + } + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.5 - theme.edgeInset.height * 2 - kOffsetGap; + _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, textHeightLimit); + [_view.textView invalidateTextContainerOrigin]; + + // resize background image, if any + if (theme.backgroundImage && theme.backgroundImage.valid) { + CGFloat panelWidth = _textWidthLimit + theme.separatorWidth; + NSSize backgroundImageSize = theme.backgroundImage.size; + theme.backgroundImage.resizingMode = NSImageResizingModeStretch; + theme.backgroundImage.size = theme.vertical ? + NSMakeSize(backgroundImageSize.width / backgroundImageSize.height * panelWidth, panelWidth) : + NSMakeSize(panelWidth, backgroundImageSize.height / backgroundImageSize.width * panelWidth); } - _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX); } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { - NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: - (_view.appear == darkAppear ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; + NSAppearanceName appearanceName = _view.appear == darkAppear ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua; + NSAppearance *requestedAppearance = [NSAppearance appearanceNamed:appearanceName]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; } @@ -1823,34 +2176,37 @@ - (void)show { //Break line if the text is too long, based on screen size. SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; - NSEdgeInsets insets = _view.insets; - CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - NSRect screenRect = [[NSScreen mainScreen] visibleFrame]; - CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; + NSEdgeInsets insets = _view.alignmentRectInsets; + CGFloat textWidthRatio = MIN(0.5, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + NSRect screenRect = self.screen.visibleFrame; + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.5 - insets.top - insets.bottom - kOffsetGap; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); NSRect contentRect = _view.contentRect; NSRect maxContentRect = contentRect; - if (theme.lineLength > 0) { // fixed line length (text width) - if (_statusMessage == nil) { // not applicable to status message - maxContentRect.size.width = _textWidthLimit; - } - } - if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) - : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) - : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect)))) && - theme.lineLength == 0) { + // fixed line length (text width), but not applicable to status message + if (theme.lineLength > 0 && _statusMessage == nil) { + maxContentRect.size.width = _textWidthLimit; + } + // remember panel size (fix the top leading anchor of the panel in screen coordiantes) + // but only when the text would expand on the side of upstream (i.e. towards the beginning of text) + if (theme.rememberSize && _statusMessage == nil) { + if (theme.lineLength == 0 && (theme.vertical + ? (sweepVertical ? (NSMinY(_position) - MAX(NSWidth(maxContentRect), _maxSize.width) - insets.right < NSMinY(screenRect)) + : (NSMinY(_position) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect))) + : (sweepVertical ? (NSMinX(_position) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) + : (NSMaxX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect))))) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { maxContentRect.size.width = _maxSize.width; - [textContainer setSize:NSMakeSize(_maxSize.width, textHeightLimit)]; + textContainer.size = NSMakeSize(_maxSize.width, textHeightLimit); } } - if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) - : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : kOffsetHeight))) { + CGFloat textHeight = MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom; + if (theme.vertical ? (NSMinX(_position) - textHeight - (sweepVertical ? kOffsetGap : 0) < NSMinX(screenRect)) + : (NSMinY(_position) - textHeight - (sweepVertical ? 0 : kOffsetGap) < NSMinY(screenRect))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { @@ -1870,11 +2226,11 @@ - (void)show { windowRect.size.height = NSHeight(maxContentRect) + insets.top + insets.bottom; } if (sweepVertical) { // vertically centre-align (MidY) in screen coordinates - windowRect.origin.x = NSMinX(_position) - kOffsetHeight - windowRect.size.width; - windowRect.origin.y = NSMidY(_position) - windowRect.size.height / 2; + windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); + windowRect.origin.y = NSMidY(_position) - NSHeight(windowRect) * 0.5; } else { // horizontally centre-align (MidX) in screen coordinates - windowRect.origin.x = NSMidX(_position) - windowRect.size.width / 2; - windowRect.origin.y = NSMinY(_position) - kOffsetHeight - windowRect.size.height; + windowRect.origin.x = NSMidX(_position) - NSWidth(windowRect) * 0.5; + windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); } } else { if (theme.vertical) { // anchor is the top right corner in screen coordinates (MaxX, MaxY) @@ -1884,17 +2240,26 @@ - (void)show { NSWidth(maxContentRect) + insets.left + insets.right); _initPosition |= NSIntersectsRect(windowRect, _position); if (_initPosition) { - // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : insets.left + kOffsetHeight) - NSWidth(maxContentRect) - insets.right; + if (!sweepVertical) { + // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa + if (NSMinY(_position) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect)) { + windowRect.origin.y = NSMaxY(_position) + kOffsetGap; + } else { + windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); + } + // Make the right edge of candidate block fixed at the left of cursor + windowRect.origin.x = NSMinX(_position) + insets.top - NSWidth(windowRect); + if (_view.preeditRange.length > 0) { + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.x += NSHeight(preeditRect); + } } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); - } - // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSHeight(maxContentRect) - insets.top - insets.bottom; - if (!sweepVertical && _view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); + if (NSMinX(_position) - kOffsetGap - NSWidth(windowRect) < NSMinX(screenRect)) { + windowRect.origin.x = NSMaxX(_position) + kOffsetGap; + } else { + windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); + } + windowRect.origin.y = NSMinY(_position) + insets.left - NSHeight(windowRect); } } } else { // anchor is the top left corner in screen coordinates (MinX, MaxY) @@ -1906,71 +2271,84 @@ - (void)show { if (_initPosition) { if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { - windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + if (NSMinX(_position) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) { + windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + windowRect.origin.x = NSMaxX(_position) + kOffsetGap; + } + windowRect.origin.y = NSMinY(_position) + insets.top - NSHeight(windowRect); + if (_view.preeditRange.length > 0) { + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.y += NSHeight(preeditRect); } - windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); } else { - windowRect.origin.x = NSMinX(_position) - insets.left; - windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); + if (NSMinY(_position) - kOffsetGap - NSHeight(windowRect) < NSMinY(screenRect)) { + windowRect.origin.y = NSMaxY(_position) + kOffsetGap; + } else { + windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); + } + windowRect.origin.x = NSMaxX(_position) - insets.left; } } } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_position) - kOffsetHeight, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_position) - kOffsetGap, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_position) + kOffsetHeight, NSMinX(screenRect)) : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_position) + kOffsetGap, NSMinX(screenRect)) : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_position) + kOffsetHeight, NSMinY(screenRect)) : NSMinY(screenRect); + windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_position) + kOffsetGap, NSMinY(screenRect)) : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_position) - kOffsetHeight, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_position) - kOffsetGap, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); } + windowRect = NSIntersectionRect(windowRect, screenRect); if (theme.vertical) { - windowRect.origin.x += round(NSHeight(maxContentRect) - NSHeight(contentRect)); - windowRect.size.width -= round(NSHeight(maxContentRect) - NSHeight(contentRect)); + windowRect.origin.x += NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.size.width -= NSHeight(maxContentRect) - NSHeight(contentRect); } else { - windowRect.origin.y += round(NSHeight(maxContentRect) - NSHeight(contentRect)); - windowRect.size.height -= round(NSHeight(maxContentRect) - NSHeight(contentRect)); + windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); + windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); } // rotate the view, the core in vertical mode! if (theme.vertical) { - [self setFrame:[[NSScreen mainScreen] backingAlignedRect:windowRect options:NSAlignMaxXOutward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; - [self.contentView setBoundsRotation:-90.0]; + windowRect = [self.screen backingAlignedRect:windowRect options:NSAlignAllEdgesNearest]; + [self setFrame:windowRect display:YES]; + self.contentView.boundsRotation = -90.0; [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; } else { - [self setFrame:[[NSScreen mainScreen] backingAlignedRect:windowRect options:NSAlignMinXInward | NSAlignMaxYInward | NSAlignWidthNearest | NSAlignHeightNearest] display:NO]; - [self.contentView setBoundsRotation:0.0]; + windowRect = [self.screen backingAlignedRect:windowRect options:NSAlignAllEdgesNearest]; + [self setFrame:windowRect display:YES]; + self.contentView.boundsRotation = 0.0; [self.contentView setBoundsOrigin:NSZeroPoint]; } - NSRect frameRect = self.contentView.bounds; - NSRect textFrameRect = NSMakeRect(NSMinX(frameRect) + insets.left, NSMinY(frameRect) + insets.bottom, - NSWidth(frameRect) - insets.left - insets.right, - NSHeight(frameRect) - insets.top - insets.bottom); - [_view.textView setBoundsRotation:0.0]; - [_view setBoundsOrigin:NSMakePoint(-insets.left, -insets.top)]; + NSRect viewRect = self.contentView.bounds; + NSRect textViewRect = NSMakeRect(NSMinX(viewRect) + insets.left - _view.textView.textContainerOrigin.x, + NSMinY(viewRect) + insets.bottom - _view.textView.textContainerOrigin.y, + NSWidth(viewRect) - insets.left - insets.right, + NSHeight(viewRect) - insets.top - insets.bottom); + textViewRect = [self.contentView backingAlignedRect:textViewRect options:NSAlignAllEdgesNearest]; + NSPoint viewOrigin = NSMakePoint(NSMinX(viewRect) - NSMinX(textViewRect), NSMaxY(textViewRect) - NSMaxY(viewRect)); + _view.textView.boundsRotation = 0.0; + [_view setBoundsOrigin:viewOrigin]; [_view.textView setBoundsOrigin:NSZeroPoint]; - [_view setFrame:frameRect]; - [_view.textView setFrame:textFrameRect]; + _view.frame = viewRect; + _view.textView.frame = textViewRect; if (theme.translucency > 0) { - [_back setBoundsOrigin:NSMakePoint(-insets.left, -insets.top)]; - [_back setFrame:frameRect]; - [_back setAppearance:NSApp.effectiveAppearance]; + [_back setBoundsOrigin:viewOrigin]; + _back.frame = viewRect; + _back.appearance = NSApp.effectiveAppearance; [_back setHidden:NO]; } else { [_back setHidden:YES]; } - [self setAlphaValue:theme.alpha]; - [self display]; + self.alphaValue = theme.alpha; [self orderFront:nil]; // reset to initial position after showing status message _initPosition = _statusMessage != nil; @@ -1987,88 +2365,6 @@ - (void)hide { _initPosition = YES; } -- (void)setLayoutForRange:(NSRange)charRange { - BOOL verticalLayout = _view.currentTheme.vertical; - NSFont *refFont = [_view.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) - atIndex:charRange.location - effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - if (@available(macOS 12.0, *)) { - [_view.textStorage - enumerateAttributesInRange:charRange - options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue]; - NSFont *runFont = attrs[NSFontAttributeName]; - NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; - if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { - if (verticalLayout) { - baselineOffset -= superscript * (runFont.ascender - runFont.descender) / 16; - } else if (superscript == -1) { - baselineOffset -= runFont.descender; - } else if (superscript == 1) { - baselineOffset -= runFont.underlinePosition; - } - } else if (superscript != 0) { - baselineOffset -= runFont.descender; - } - [_view.textStorage addAttribute:NSBaselineOffsetAttributeName - value:@(baselineOffset) range:range]; - }]; - } else { - NSParagraphStyle *style = [_view.textStorage attribute:NSParagraphStyleAttributeName - atIndex:charRange.location effectiveRange:NULL]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; - NSLayoutManager *layoutManager = _view.textView.layoutManager; - NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; - [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange - usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange range, BOOL *stop) { - CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); - // typesetting glyphs - NSUInteger j = range.location; - while (j < NSMaxRange(range)) { - NSPoint runGlyphPosition = [layoutManager locationForGlyphAtIndex:j]; - NSUInteger runCharLocation = [layoutManager characterIndexForGlyphAtIndex:j]; - NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; - NSDictionary *attrs = [layoutManager.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; - NSFont *runFont = attrs[NSFontAttributeName]; - NSFont *resizedRefFont = [NSFont fontWithDescriptor:refFont.fontDescriptor size:runFont.pointSize]; - NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; - NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; - CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; - NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; - if (verticalLayout) { - NSNumber *verticalGlyph = attrs[NSVerticalGlyphFormAttributeName]; - if (verticalGlyph ? verticalGlyph.boolValue : YES) { - runFont = runFont.verticalFont; - resizedRefFont = resizedRefFont.verticalFont; - } - } - if (superscript != 0) { - offset += superscript == 1 ? refFont.ascender - resizedRefFont.ascender : refFont.descender - resizedRefFont.descender; - if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { - offset -= (runFont.ascender - runFont.descender) / 16; - } - } - if (verticalLayout) { - if ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassRoman)] || !runFont.vertical) { - runGlyphPosition.y = alignment - offset + resizedRefFont.xHeight / 2; - } else { - runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] && superscript == 0 ? (runFont.ascender - runFont.descender) / 16 : 0.0); - runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFont.ascender - runFont.descender) / 8 : 0.0; - } - } else { - runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - resizedRefFont.xHeight / 2 : 0.0); - } - [layoutManager setLocation:runGlyphPosition forStartOfGlyphRange:runRange]; - j = NSMaxRange(runRange); - } - }]; - } -} - - (BOOL)shouldBreakLineInsideRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; NSUInteger __block lineCount = 0; @@ -2076,9 +2372,10 @@ - (BOOL)shouldBreakLineInsideRange:(NSRange)range { NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard + type:NSTextLayoutManagerSegmentTypeHighlight options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, + NSTextContainer *textContainer) { lineCount += 1 + (NSMaxX(segFrame) > _textWidthLimit); return YES; }]; @@ -2086,74 +2383,62 @@ - (BOOL)shouldBreakLineInsideRange:(NSRange)range { NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; [_view.textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange - usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { - lineCount += 1 + (NSMaxX(usedRect) > self->_textWidthLimit); + usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, + NSRange lineRange, BOOL *stop) { + lineCount += 1 + (NSMaxX(usedRect) > self->_textWidthLimit); }]; } return lineCount > 1; } -- (BOOL)shouldUseTabsInRange:(NSRange)range maxLineLength:(CGFloat *)maxLineLength { +- (BOOL)shouldUseTabInRange:(NSRange)range + maxLineLength:(CGFloat *)maxLineLength { [_view.textStorage fixFontAttributeInRange:range]; + if (_view.currentTheme.lineLength > 0) { + *maxLineLength = MAX(_textWidthLimit, _maxSize.width); + return YES; + } if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; CGFloat __block rangeEndEdge; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard + type:NSTextLayoutManagerSegmentTypeHighlight options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { + usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, + NSTextContainer *textContainer) { rangeEndEdge = NSMaxX(segFrame); return YES; }]; [_view.textView.textLayoutManager ensureLayoutForRange:_view.textView.textContentStorage.documentRange]; - NSRect container = [_view.textView.textLayoutManager usageBoundsForTextContainer]; - *maxLineLength = MAX(MIN(_textWidthLimit, NSMaxX(container)), _maxSize.width); + NSRect container = _view.textView.textLayoutManager.usageBoundsForTextContainer; + *maxLineLength = MAX(*maxLineLength, MAX(NSMaxX(container), _maxSize.width)); return *maxLineLength > rangeEndEdge; } else { NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; - CGFloat rangeEndEdge = NSMaxX([_view.textView.layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); + CGFloat rangeEndEdge = NSMaxX([_view.textView.layoutManager + lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); NSRect container = [_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer]; - *maxLineLength = MAX(MIN(_textWidthLimit, NSMaxX(container)), _maxSize.width); + *maxLineLength = MAX(*maxLineLength, MAX(NSMaxX(container), _maxSize.width)); return *maxLineLength > rangeEndEdge; } } -- (CGFloat)getInlineOffsetAfterCharacterRange:(NSRange)range { - if (@available(macOS 12.0, *)) { - NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; - CGFloat __block offset; - [_view.textView.textLayoutManager - enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeStandard - options:NSTextLayoutManagerSegmentOptionsUpstreamAffinity - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { - offset = NSMaxX(segFrame); - return NO; - }]; - return offset; - } else { - NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; - NSRect boundingRect = [_view.textView.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:_view.textView.textContainer]; - return NSMaxX(boundingRect); - } -} - // Main function to add attributes to text output from librime - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - highlighted:(NSUInteger)index + highlighted:(NSUInteger)highlighted pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage { - [self getTextWidthLimit]; _numCandidates = candidates.count; - _index = _numCandidates == 0 ? NSNotFound : index; + _highlighted = _numCandidates == 0 ? NSNotFound : highlighted; + _caretAtHome = caretPos == NSNotFound || (caretPos == selRange.location && selRange.location == 1); _firstPage = pageNum == 0; _lastPage = lastPage; - _turnPage = NSNotFound; + _functionButton = kVoidSymbol; if (_numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { @@ -2170,6 +2455,10 @@ - (void)showPreedit:(NSString *)preedit return; } + if (!NSIntersectsRect(_position, self.screen.frame)) { + [self updateDisplayParameters]; + } + SquirrelTheme *theme = _view.currentTheme; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -2181,17 +2470,24 @@ - (void)showPreedit:(NSString *)preedit [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); - NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:_numCandidates]; + NSMutableArray *candidateRanges = + [[NSMutableArray alloc] initWithCapacity:_numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); + + CGFloat topMargin = preedit ? 0.0 : ceil(theme.linespace * 0.5); + CGFloat bottomMargin = _numCandidates > 0 && (theme.linear || !theme.showPaging) ? + floor(theme.linespace * 0.5) : 0.0; NSMutableParagraphStyle *paragraphStyleCandidate; + CGFloat tabInterval = theme.separatorWidth * 2; + CGFloat maxLineLength = 0.0; // preedit if (preedit) { NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { - [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringToIndex:selRange.location] - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString: + [[NSAttributedString alloc] initWithString:[preedit substringToIndex:selRange.location] + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { NSUInteger highlightedPreeditStart = preeditLine.length; @@ -2202,22 +2498,38 @@ - (void)showPreedit:(NSString *)preedit preeditLine.length - highlightedPreeditStart); } if (NSMaxRange(selRange) < preedit.length) { - [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:[preedit substringFromIndex:NSMaxRange(selRange)] - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString: + [[NSAttributedString alloc] initWithString:[preedit substringFromIndex:NSMaxRange(selRange)] + attributes:theme.preeditAttrs]]; } - // force caret to be rendered horizontally in vertical layout + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:kFullWidthSpace attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:_caretAtHome ? theme.symbolDeleteStroke : theme.symbolDeleteFill]; + // force caret to be rendered sideways, instead of uprights, in vertical orientation if (caretPos != NSNotFound) { - [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@NO + [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@(NO) range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; if (_numCandidates > 0) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] + initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { - goto typesetter; + if ([self shouldUseTabInRange:NSMakeRange(preeditLine.length - 2, 2) + maxLineLength:&maxLineLength]) { + if (theme.tabled) { + maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; + } + [text replaceCharactersInRange:NSMakeRange(preeditLine.length - 2, 1) withString:@"\t"]; + NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; + paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength options:@{}]]; + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePreedit range:preeditRange]; + } + goto drawPanel; } } @@ -2227,94 +2539,95 @@ - (void)showPreedit:(NSString *)preedit if (theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle copy]; } - CGFloat tabInterval = theme.separatorWidth * 2; for (NSUInteger idx = 0; idx < _numCandidates; ++idx) { // attributed labels are already included in candidateFormats - NSMutableAttributedString *item = (idx == index) ? [theme.candidateHighlightedFormats[idx] mutableCopy] : [theme.candidateFormats[idx] mutableCopy]; + NSMutableAttributedString *item = (idx == highlighted) ? [theme.candidateHighlightedFormats[idx] mutableCopy] + : [theme.candidateFormats[idx] mutableCopy]; NSRange candidateRange = [item.string rangeOfString:@"%@"]; // get the label size for indent CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange:NSMakeRange(0, candidateRange.location)].size.width); - - [item replaceCharactersInRange:candidateRange withString:candidates[idx]]; + // plug in candidate texts and comments into the template + [item replaceCharactersInRange:candidateRange + withString:candidates[idx]]; NSRange commentRange = [item.string rangeOfString:kTipSpecifier]; - if ([comments[idx] length] != 0) { - [item replaceCharactersInRange:commentRange withString:[@" " stringByAppendingString:comments[idx]]]; + if (comments[idx].length != 0) { + [item replaceCharactersInRange:commentRange + withString:[@" " stringByAppendingString:comments[idx]]]; } else { [item deleteCharactersInRange:commentRange]; } [item formatMarkDown]; - CGFloat annotationHeight = [item annotateRubyInRange:NSMakeRange(0, item.length) verticalLayout:theme.vertical maximumLength:_textWidthLimit]; + CGFloat annotationHeight = [item annotateRubyInRange:NSMakeRange(0, item.length) + verticalOrientation:theme.vertical maximumLength:_textWidthLimit]; if (annotationHeight * 2 > theme.linespace) { [self setAnnotationHeight:annotationHeight]; - paragraphStyleCandidate = [theme.paragraphStyle copy]; - [text enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart) options:0 + paragraphStyleCandidate = theme.paragraphStyle.copy; + [text enumerateAttribute:NSParagraphStyleAttributeName + inRange:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart) options:0 usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL *stop) { - NSMutableParagraphStyle *style = [value mutableCopy]; + NSMutableParagraphStyle *style = value.mutableCopy; style.paragraphSpacing = annotationHeight; style.paragraphSpacingBefore = annotationHeight; [text addAttribute:NSParagraphStyleAttributeName value:style range:range]; }]; + topMargin = preedit ? 0.0 : annotationHeight; + bottomMargin = (theme.linear || !theme.showPaging) ? annotationHeight : 0.0; } - if ([comments[idx] length] != 0 && [item.string hasSuffix:@" "]) { + if (comments[idx].length != 0 && [item.string hasSuffix:@" "]) { [item deleteCharactersInRange:NSMakeRange(item.length - 1, 1)]; } if (!theme.linear) { - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + paragraphStyleCandidate = theme.paragraphStyle.mutableCopy; paragraphStyleCandidate.headIndent = labelWidth; } [item addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(0, item.length)]; + value:paragraphStyleCandidate range:NSMakeRange(0, item.length)]; // determine if the line is too wide and line break is needed, based on screen size. if (lineStart != text.length) { NSUInteger separatorStart = text.length; // separator: linear = " "; tabled = " \t"; stacked = "\n" - NSMutableAttributedString *separator = [theme.separator mutableCopy]; - if (theme.tabled) { // fill gaps to make cells 2N-tab wide - CGFloat widthInTabs = ([self getInlineOffsetAfterCharacterRange:candidateRanges.lastObject.rangeValue] + theme.separatorWidth) / tabInterval; - NSUInteger numPaddingTabs = (NSUInteger)ceil(ceil(widthInTabs / 2) * 2 - widthInTabs) - 1; - [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; - } + NSMutableAttributedString *separator = theme.separator.mutableCopy; [text appendAttributedString:separator]; [text appendAttributedString:item]; - if (theme.linear && (ceil(item.size.width + theme.separatorWidth) > _textWidthLimit || + if (theme.linear && (ceil(item.size.width) > _textWidthLimit || [self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)])) { - [text replaceCharactersInRange:NSMakeRange(separatorStart + 1, separator.length - 1) withString:@"\n"]; - lineStart = separatorStart + 2; + [text replaceCharactersInRange:NSMakeRange(separatorStart, separator.length) withString:@"\n"]; + lineStart = separatorStart + 1; } } else { // at the start of a new line, no need to determine line break [text appendAttributedString:item]; } // for linear layout, middle-truncate candidates that are longer than one line - if (theme.linear && ceil(item.size.width + theme.separatorWidth) > _textWidthLimit) { + if (theme.linear && ceil(item.size.width) > _textWidthLimit) { if (idx < _numCandidates - 1 || theme.showPaging) { - [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] + initWithString:@"\n" attributes:theme.commentAttrs]]; } - NSMutableParagraphStyle *paragraphStyleTruncating = [paragraphStyleCandidate mutableCopy]; + NSMutableParagraphStyle *paragraphStyleTruncating = paragraphStyleCandidate.mutableCopy; paragraphStyleTruncating.lineBreakMode = NSLineBreakByTruncatingMiddle; - [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleTruncating range:NSMakeRange(lineStart, item.length)]; - [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(lineStart, item.length)]]; + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleTruncating + range:NSMakeRange(lineStart, item.length)]; + [candidateRanges addObject:[NSValue valueWithRange: + NSMakeRange(lineStart, item.length)]]; lineStart = text.length; } else { - [candidateRanges addObject:[NSValue valueWithRange:NSMakeRange(text.length - item.length, item.length)]]; + [candidateRanges addObject:[NSValue valueWithRange: + NSMakeRange(text.length - item.length, item.length)]]; } } // paging indication if (theme.showPaging) { - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] - initWithAttributedString:(pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke)]; - [paging appendAttributedString:[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; - [paging appendAttributedString:[[NSAttributedString alloc] - initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]]; - + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString: + [NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]; + [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke atIndex:0]; + [paging appendAttributedString:lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill]; [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; - CGFloat maxLineLength; [text appendAttributedString:paging]; if (theme.linear) { if ([self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)]) { @@ -2322,28 +2635,38 @@ - (void)showPreedit:(NSString *)preedit lineStart = pagingStart; pagingStart += 1; } - if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; - paragraphStyleCandidate = [theme.paragraphStyle mutableCopy]; + if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) + maxLineLength:&maxLineLength] || + lineStart != candidateBlockStart) { + if (theme.tabled) { + maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; + } else { + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; + } + paragraphStyleCandidate = theme.paragraphStyle.mutableCopy; paragraphStyleCandidate.tabStops = @[]; - CGFloat candidateEndPosition = ceil([self getInlineOffsetAfterCharacterRange:NSMakeRange(lineStart, pagingStart - 1 - lineStart)]); - CGFloat textPostion = tabInterval; - while (textPostion <= candidateEndPosition) { - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft - location:textPostion options:@{}]]; - textPostion += tabInterval; + NSUInteger candidateEnd = pagingStart - 1; + CGFloat candidateEndPosition = NSMaxX([_view contentRectForRange: + NSMakeRange(lineStart, candidateEnd - lineStart)]); + for (NSUInteger i = 1; i * tabInterval < candidateEndPosition; ++i) { + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] + initWithTextAlignment:NSTextAlignmentLeft + location:i * tabInterval options:@{}]]; } - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength options:@{}]]; + [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] + initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate - range:NSMakeRange(lineStart, text.length - lineStart)]; + value:paragraphStyleCandidate range:NSMakeRange(lineStart, text.length - lineStart)]; } else { - NSMutableParagraphStyle *paragraphStylePaging = [theme.pagingParagraphStyle mutableCopy]; - if ([self shouldUseTabsInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1) withString:@"\t"]; - [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) withString:@"\t"]; + NSMutableParagraphStyle *paragraphStylePaging = theme.pagingParagraphStyle.mutableCopy; + if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) + maxLineLength:&maxLineLength]) { + [text replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1) + withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) + withString:@"\t"]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:maxLineLength / 2 options:@{}], [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight @@ -2355,38 +2678,34 @@ - (void)showPreedit:(NSString *)preedit } pagingRange = NSMakeRange(text.length - paging.length, paging.length); } - -typesetter: - [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + theme.linespace / 2, - theme.edgeInset.width + theme.separatorWidth / 2, - theme.edgeInset.height + theme.linespace / 2, - theme.edgeInset.width + theme.separatorWidth / 2); - if (preedit) { - [self setLayoutForRange:preeditRange]; - insets.top = theme.edgeInset.height; - } - if (_numCandidates > 0) { - NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); - [self setLayoutForRange:candidateBlockRange]; - if (!theme.linear && pagingRange.length > 0) { - [self setLayoutForRange:pagingRange]; - insets.bottom = theme.edgeInset.height; - } - } else { - insets.bottom = theme.edgeInset.height; + // right-align the backward delete symbol + if (preedit && [self shouldUseTabInRange:NSMakeRange(preeditRange.length - 2, 2) + maxLineLength:&maxLineLength]) { + [text replaceCharactersInRange:NSMakeRange(preeditRange.length - 2, 1) + withString:@"\t"]; + NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; + paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] + initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength options:@{}]]; + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStylePreedit range:preeditRange]; } // text done! - [self setAnimationBehavior:caretPos == NSNotFound ? - NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault]; +drawPanel: + [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + topMargin, + theme.edgeInset.width + ceil(theme.separatorWidth * 0.5), + theme.edgeInset.height + bottomMargin, + theme.edgeInset.width + floor(theme.separatorWidth * 0.5)); + self.animationBehavior = caretPos == NSNotFound ? + NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault; [_view drawViewWithInsets:insets candidateRanges:candidateRanges - highlightedIndex:index + highlightedIndex:highlighted preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange - pagingRange:pagingRange - pagingButton:_turnPage]; + pagingRange:pagingRange]; [self show]; } @@ -2405,38 +2724,40 @@ - (void)updateStatusLong:(NSString *)messageLong if (messageShort) { _statusMessage = messageShort; } else if (messageLong) { - _statusMessage = [messageLong substringWithRange:[messageLong rangeOfComposedCharacterSequenceAtIndex:0]]; + _statusMessage = [messageLong substringWithRange: + [messageLong rangeOfComposedCharacterSequenceAtIndex:0]]; } } } - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, theme.edgeInset.width + theme.separatorWidth / 2, - theme.edgeInset.height, theme.edgeInset.width + theme.separatorWidth / 2); + NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, + theme.edgeInset.width + ceil(theme.separatorWidth * 0.5), + theme.edgeInset.height, + theme.edgeInset.width + floor(theme.separatorWidth * 0.5)); _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; NSTextStorage *text = _view.textStorage; - [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; + [text setAttributedString:[[NSAttributedString alloc] + initWithString:message + attributes:theme.statusAttrs]]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; - [self setLayoutForRange:NSMakeRange(0, text.length)]; - // disable remember_size and fixed line_length for status messages _initPosition = YES; _maxSize = NSZeroSize; if (_statusTimer) { [_statusTimer invalidate]; } - [self setAnimationBehavior:NSWindowAnimationBehaviorUtilityWindow]; + self.animationBehavior = NSWindowAnimationBehaviorUtilityWindow; [_view drawViewWithInsets:insets candidateRanges:@[] highlightedIndex:NSNotFound preeditRange:NSMakeRange(NSNotFound, 0) highlightedPreeditRange:NSMakeRange(NSNotFound, 0) - pagingRange:NSMakeRange(NSNotFound, 0) - pagingButton:NSNotFound]; + pagingRange:NSMakeRange(NSNotFound, 0)]; [self show]; _statusTimer = [NSTimer scheduledTimerWithTimeInterval:kShowStatusDuration target:self @@ -2452,50 +2773,44 @@ - (void)hideStatus:(NSTimer *)timer { static inline NSColor * blendColors(NSColor *foregroundColor, NSColor *backgroundColor) { if (!backgroundColor) { // return foregroundColor; - backgroundColor = [NSColor lightGrayColor]; + backgroundColor = NSColor.lightGrayColor; } return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:backgroundColor] colorWithAlphaComponent:foregroundColor.alphaComponent]; } -static inline NSColor * inverseColor(NSColor *color) { - if (color == nil) { - return nil; - } else { - return [NSColor colorWithColorSpace:color.colorSpace - hue:color.hueComponent - saturation:color.saturationComponent - brightness:1 - color.brightnessComponent - alpha:color.alphaComponent]; - } -} - static NSFontDescriptor * getFontDescriptor(NSString *fullname) { if (fullname == nil) { return nil; } NSArray *fontNames = [fullname componentsSeparatedByString:@","]; - NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] initWithCapacity:fontNames.count]; + NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] + initWithCapacity:fontNames.count]; for (__strong NSString *fontName in fontNames) { - fontName = [fontName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + fontName = [fontName stringByTrimmingCharactersInSet: + NSCharacterSet.whitespaceAndNewlineCharacterSet]; if ([NSFont fontWithName:fontName size:0.0] != nil) { // If the font name is not valid, NSFontDescriptor will still create something for us. // However, when we draw the actual text, Squirrel will crash if there is any font descriptor // with invalid font name. - NSFontDescriptor *fontDescriptor = [NSFontDescriptor fontDescriptorWithName:fontName size:0.0]; - NSFontDescriptor *UIFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized]; - [validFontDescriptors addObject:([NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil ? UIFontDescriptor : fontDescriptor)]; + NSFontDescriptor *fontDescriptor = + [NSFontDescriptor fontDescriptorWithName:fontName size:0.0]; + NSFontDescriptor *UIFontDescriptor = + [fontDescriptor fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized]; + [validFontDescriptors addObject:([NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil + ? UIFontDescriptor : fontDescriptor)]; } } if (validFontDescriptors.count == 0) { return nil; } NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; + NSFontDescriptor *emojiFontDescriptor = + [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] - arrayByAddingObject:emojiFontDescriptor]; + arrayByAddingObject:emojiFontDescriptor]; NSDictionary *attributes = @{NSFontCascadeListAttribute: fallbackDescriptors}; return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; } @@ -2505,9 +2820,11 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { font = font.verticalFont; } CGFloat lineHeight = font.ascender - font.descender; - NSArray *fallbackList = [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + NSArray *fallbackList = + [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback + size:font.pointSize]; if (vertical) { fallbackFont = fallbackFont.verticalFont; } @@ -2516,7 +2833,7 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return lineHeight; } -static NSFont * getTallestFont(NSArray*fonts, BOOL vertical) { +static NSFont * getTallestFont(NSArray *fonts, BOOL vertical) { NSFont *tallestFont; CGFloat maxHeight = 0.0; for (NSFont *font in fonts) { @@ -2529,8 +2846,10 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return tallestFont; } -static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, SquirrelConfig *config, NSString *prefix) { - NSString *candidateListLayout = [config getString:[prefix stringByAppendingString:@"/candidate_list_layout"]]; +static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, + SquirrelConfig *config, NSString *prefix) { + NSString *candidateListLayout = [config getString: + [prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { *isLinearCandidateList = NO; *isTabledCandidateList = NO; @@ -2543,7 +2862,8 @@ static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTable *isTabledCandidateList = YES; } else { // Deprecated. Not to be confused with text_orientation: horizontal - NSNumber *horizontal = [config getOptionalBool:[prefix stringByAppendingString:@"/horizontal"]]; + NSNumber *horizontal = [config getOptionalBool: + [prefix stringByAppendingString:@"/horizontal"]]; if (horizontal) { *isLinearCandidateList = horizontal.boolValue; *isTabledCandidateList = NO; @@ -2552,13 +2872,15 @@ static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTable } static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, NSString *prefix) { - NSString *textOrientation = [config getString:[prefix stringByAppendingString:@"/text_orientation"]]; + NSString *textOrientation = [config getString: + [prefix stringByAppendingString:@"/text_orientation"]]; if ([textOrientation isEqualToString:@"horizontal"]) { *isVerticalText = NO; } else if ([textOrientation isEqualToString:@"vertical"]) { *isVerticalText = YES; } else { - NSNumber *vertical = [config getOptionalBool:[prefix stringByAppendingString:@"/vertical"]]; + NSNumber *vertical = [config getOptionalBool: + [prefix stringByAppendingString:@"/vertical"]]; if (vertical) { *isVerticalText = vertical.boolValue; } @@ -2566,16 +2888,23 @@ static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, } - (void)setAnnotationHeight:(CGFloat)height { - [[_view selectTheme:NO] setAnnotationHeight:height]; - [[_view selectTheme:YES] setAnnotationHeight:height]; + [[_view selectTheme:defaultAppear] setAnnotationHeight:height]; + [[_view selectTheme:darkAppear] setAnnotationHeight:height]; } - (void)loadLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update { SquirrelTheme *theme = [_view selectTheme:defaultAppear]; - [SquirrelPanel updateTheme:theme withLabelConfig:config directUpdate:update]; + [SquirrelPanel updateTheme:theme + withLabelConfig:config + directUpdate:update]; SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; - [SquirrelPanel updateTheme:darkTheme withLabelConfig:config directUpdate:update]; + [SquirrelPanel updateTheme:darkTheme + withLabelConfig:config + directUpdate:update]; + if (update) { + [self updateDisplayParameters]; + } } + (void)updateTheme:(SquirrelTheme *)theme @@ -2592,8 +2921,9 @@ + (void)updateTheme:(SquirrelTheme *)theme } if (selectKeys) { if (!selectLabels) { - NSString *keyCaps = [[selectKeys uppercaseString] - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *keyCaps = [selectKeys.uppercaseString + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; } @@ -2601,22 +2931,36 @@ + (void)updateTheme:(SquirrelTheme *)theme } else { selectKeys = [@"1234567890" substringToIndex:menuSize]; if (!selectLabels) { - NSString *numerals = [selectKeys stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + NSString *numerals = [selectKeys + stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth + reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; } } } - [theme setSelectKeys:selectKeys labels:labels directUpdate:update]; + [theme setSelectKeys:selectKeys + labels:labels + directUpdate:update]; } - (void)loadConfig:(SquirrelConfig *)config forAppearance:(SquirrelAppear)appear { SquirrelTheme *theme = [_view selectTheme:appear]; NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; - [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forAppearance:appear]; + [SquirrelPanel updateTheme:theme + withConfig:config + styleOptions:styleOptions + forAppearance:appear]; + [self updateDisplayParameters]; } +// functions for post-retrieve processing +double positive(double param) { return fmax(0.0, param); } +double pos_round(double param) { return round(fmax(0.0, param)); } +double pos_ceil(double param) { return ceil(fmax(0.0, param)); } +double clamp_uni(double param) { return fmin(1.0, fmax(0.0, param)); } + + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config styleOptions:(NSSet *)styleOptions @@ -2635,24 +2979,35 @@ + (void)updateTheme:(SquirrelTheme *)theme NSString *candidateFormat = [config getString:@"style/candidate_format"]; // TYPOGRAPHY NSString *fontName = [config getString:@"style/font_face"]; - NSNumber *fontSize = [config getOptionalDouble:@"style/font_point"]; + NSNumber *fontSize = [config getOptionalDouble:@"style/font_point" + applyConstraint:pos_round]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point"]; + NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point" + applyConstraint:pos_round]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSNumber *commentFontSize = [config getOptionalDouble:@"style/comment_font_point"]; - NSNumber *alpha = [config getOptionalDouble:@"style/alpha"]; - NSNumber *translucency = [config getOptionalDouble:@"style/translucency"]; - NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius"]; - NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius"]; - NSNumber *borderHeight = [config getOptionalDouble:@"style/border_height"]; - NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width"]; - NSNumber *lineSpacing = [config getOptionalDouble:@"style/line_spacing"]; - NSNumber *spacing = [config getOptionalDouble:@"style/spacing"]; + NSNumber *commentFontSize = [config getOptionalDouble:@"style/comment_font_point" + applyConstraint:pos_round]; + NSNumber *alpha = [config getOptionalDouble:@"style/alpha" + applyConstraint:clamp_uni]; + NSNumber *translucency = [config getOptionalDouble:@"style/translucency" + applyConstraint:clamp_uni]; + NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius" + applyConstraint:positive]; + NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius" + applyConstraint:positive]; + NSNumber *borderHeight = [config getOptionalDouble:@"style/border_height" + applyConstraint:pos_ceil]; + NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width" + applyConstraint:pos_ceil]; + NSNumber *lineSpacing = [config getOptionalDouble:@"style/line_spacing" + applyConstraint:pos_round]; + NSNumber *spacing = [config getOptionalDouble:@"style/spacing" + applyConstraint:pos_round]; NSNumber *baseOffset = [config getOptionalDouble:@"style/base_offset"]; NSNumber *lineLength = [config getOptionalDouble:@"style/line_length"]; // CHROMATICS NSColor *backgroundColor; - NSColor *backgroundImage; + NSImage *backgroundImage; NSColor *borderColor; NSColor *preeditBackgroundColor; NSColor *candidateLabelColor; @@ -2669,27 +3024,30 @@ + (void)updateTheme:(SquirrelTheme *)theme NSString *colorScheme; if (appear == darkAppear) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; + if ((colorScheme = [config getString: + [NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; } colorScheme = colorScheme ? : [config getString:@"style/color_scheme_dark"]; } if (!colorScheme) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme", option]])) break; + if ((colorScheme = [config getString: + [NSString stringWithFormat:@"style/%@/color_scheme", option]])) break; } colorScheme = colorScheme ? : [config getString:@"style/color_scheme"]; } BOOL isNative = !colorScheme || [colorScheme isEqualToString:@"native"]; - NSArray *configPrefixes = isNative ? [@"style/" stringsByAppendingPaths:styleOptions.allObjects] : - [[NSArray arrayWithObject:[@"preset_color_schemes/" stringByAppendingString:colorScheme]] - arrayByAddingObjectsFromArray:[@"style/" stringsByAppendingPaths:styleOptions.allObjects]]; + NSArray *configPrefixes = isNative + ? [@"style/" stringsByAppendingPaths:styleOptions.allObjects] + : [@[[@"preset_color_schemes/" stringByAppendingString:colorScheme]] + arrayByAddingObjectsFromArray:[@"style/" stringsByAppendingPaths:styleOptions.allObjects]]; // get color scheme and then check possible overrides from styleSwitcher for (NSString *prefix in configPrefixes) { // CHROMATICS override config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backgroundColor; - backgroundImage = [config getPattern:[prefix stringByAppendingString:@"/back_image"]] ? : backgroundImage; + backgroundImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]] ? : backgroundImage; borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackgroundColor; textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]] ? : textColor; @@ -2714,23 +3072,34 @@ + (void)updateTheme:(SquirrelTheme *)theme inlineCandidate = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]] ? : inlineCandidate; showPaging = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]] ? : showPaging; rememberSize = [config getOptionalBool:[prefix stringByAppendingString:@"/remember_size"]] ? : rememberSize; - statusMessageType = [config getString:[prefix stringByAppendingString:@"style/status_message_type"]] ? : statusMessageType; + statusMessageType = [config getString:[prefix stringByAppendingString:@"/status_message_type"]] ? : statusMessageType; candidateFormat = [config getString:[prefix stringByAppendingString:@"/candidate_format"]] ? : candidateFormat; // TYPOGRAPHY override fontName = [config getString:[prefix stringByAppendingString:@"/font_face"]] ? : fontName; - fontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"]] ? : fontSize; + fontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"] + applyConstraint:pos_round] ? : fontSize; labelFontName = [config getString:[prefix stringByAppendingString:@"/label_font_face"]] ? : labelFontName; - labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"]] ? : labelFontSize; + labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"] + applyConstraint:pos_round] ? : labelFontSize; commentFontName = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]] ? : commentFontName; - commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"]] ? : commentFontSize; - alpha = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"]] ? : alpha; - translucency = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"]] ? : translucency; - cornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"]] ? : cornerRadius; - highlightedCornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"]] ? : highlightedCornerRadius; - borderHeight = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"]] ? : borderHeight; - borderWidth = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"]] ? : borderWidth; - lineSpacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"]] ? : lineSpacing; - spacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"]] ? : spacing; + commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"] + applyConstraint:pos_round] ? : commentFontSize; + alpha = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"] + applyConstraint:clamp_uni] ? : alpha; + translucency = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"] + applyConstraint:clamp_uni] ? : translucency; + cornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"] + applyConstraint:positive] ? : cornerRadius; + highlightedCornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"] + applyConstraint:positive] ? : highlightedCornerRadius; + borderHeight = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"] + applyConstraint:pos_ceil] ? : borderHeight; + borderWidth = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"] + applyConstraint:pos_ceil] ? : borderWidth; + lineSpacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"] + applyConstraint:pos_round] ? : lineSpacing; + spacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"] + applyConstraint:pos_round] ? : spacing; baseOffset = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]] ? : baseOffset; lineLength = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_length"]] ? : lineLength; } @@ -2747,67 +3116,67 @@ + (void)updateTheme:(SquirrelTheme *)theme NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0].fontName)) - size:MAX(fontSize.doubleValue, 0)]; + size:fontSize.doubleValue]; NSFontDescriptor *labelFontDescriptor = [(getFontDescriptor(labelFontName) ? : fontDescriptor) fontDescriptorByAddingAttributes:monoDigitAttrs]; - NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:MAX(labelFontSize.doubleValue, 0)] - : [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; + NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor + size:labelFontSize.doubleValue] + : [NSFont monospacedDigitSystemFontOfSize:labelFontSize.doubleValue + weight:NSFontWeightRegular]; NSString *labelString = [theme.labels componentsJoinedByString:@""]; - labelFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)labelFont, (CFStringRef)labelString, CFRangeMake(0, (int)labelString.length))); + labelFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)labelFont, (CFStringRef)labelString, + CFRangeMake(0, (CFIndex)labelString.length), CFSTR("zh"))); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) - size:MAX(commentFontSize.doubleValue, 0)]; + size:commentFontSize.doubleValue]; - NSFont *pagingFont; - if (@available(macOS 12.0, *)) { - pagingFont = [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; - } else { - pagingFont = [NSFont fontWithDescriptor:[[NSFontDescriptor fontDescriptorWithName:@"AppleSymbols" size:0] - fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized] - size:MAX(labelFontSize.doubleValue, 0)]; - } + NSFont *pagingFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize.doubleValue + weight:NSFontWeightRegular]; - CGFloat fontHeight = getLineHeight(font, vertical); - CGFloat labelFontHeight = getLineHeight(labelFont, vertical); - CGFloat commentFontHeight = getLineHeight(commentFont, vertical); - CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); - CGFloat separatorWidth = ceil([[NSAttributedString alloc] initWithString:kFullWidthSpace - attributes:@{NSFontAttributeName: commentFont}].size.width); + CGFloat fontHeight = ceil(getLineHeight(font, vertical)); + CGFloat labelFontHeight = ceil(getLineHeight(labelFont, vertical)); + CGFloat commentFontHeight = ceil(getLineHeight(commentFont, vertical)); + CGFloat lineHeight = fmax(fontHeight, fmax(labelFontHeight, commentFontHeight)); + CGFloat separatorWidth = ceil([kFullWidthSpace sizeWithAttributes:@{NSFontAttributeName: commentFont}].width); + spacing = spacing ? : @(0.0); + lineSpacing = lineSpacing ? : @(0.0); - NSMutableParagraphStyle *preeditParagraphStyle = [theme.preeditParagraphStyle mutableCopy]; + NSMutableParagraphStyle *preeditParagraphStyle = theme.preeditParagraphStyle.mutableCopy; preeditParagraphStyle.minimumLineHeight = fontHeight; preeditParagraphStyle.maximumLineHeight = fontHeight; - preeditParagraphStyle.paragraphSpacing = MAX(spacing.doubleValue, 0); + preeditParagraphStyle.paragraphSpacing = spacing.doubleValue; + preeditParagraphStyle.tabStops = @[]; - NSMutableParagraphStyle *paragraphStyle = [theme.paragraphStyle mutableCopy]; + NSMutableParagraphStyle *paragraphStyle = theme.paragraphStyle.mutableCopy; paragraphStyle.minimumLineHeight = lineHeight; paragraphStyle.maximumLineHeight = lineHeight; - paragraphStyle.paragraphSpacing = MAX(lineSpacing.doubleValue / 2, 0); - paragraphStyle.paragraphSpacingBefore = MAX(lineSpacing.doubleValue / 2, 0); + paragraphStyle.paragraphSpacingBefore = ceil(lineSpacing.doubleValue * 0.5); + paragraphStyle.paragraphSpacing = floor(lineSpacing.doubleValue * 0.5); paragraphStyle.tabStops = @[]; paragraphStyle.defaultTabInterval = separatorWidth * 2; - NSMutableParagraphStyle *pagingParagraphStyle = [theme.pagingParagraphStyle mutableCopy]; - pagingParagraphStyle.minimumLineHeight = pagingFont.ascender - pagingFont.descender; - pagingParagraphStyle.maximumLineHeight = pagingFont.ascender - pagingFont.descender; + NSMutableParagraphStyle *pagingParagraphStyle = theme.pagingParagraphStyle.mutableCopy; + pagingParagraphStyle.minimumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); + pagingParagraphStyle.maximumLineHeight = ceil(pagingFont.ascender - pagingFont.descender); + pagingParagraphStyle.tabStops = @[]; - NSMutableParagraphStyle *statusParagraphStyle = [theme.statusParagraphStyle mutableCopy]; + NSMutableParagraphStyle *statusParagraphStyle = theme.statusParagraphStyle.mutableCopy; statusParagraphStyle.minimumLineHeight = commentFontHeight; statusParagraphStyle.maximumLineHeight = commentFontHeight; - NSMutableDictionary *attrs = [theme.attrs mutableCopy]; - NSMutableDictionary *highlightedAttrs = [theme.highlightedAttrs mutableCopy]; - NSMutableDictionary *labelAttrs = [theme.labelAttrs mutableCopy]; - NSMutableDictionary *labelHighlightedAttrs = [theme.labelHighlightedAttrs mutableCopy]; - NSMutableDictionary *commentAttrs = [theme.commentAttrs mutableCopy]; - NSMutableDictionary *commentHighlightedAttrs = [theme.commentHighlightedAttrs mutableCopy]; - NSMutableDictionary *preeditAttrs = [theme.preeditAttrs mutableCopy]; - NSMutableDictionary *preeditHighlightedAttrs = [theme.preeditHighlightedAttrs mutableCopy]; - NSMutableDictionary *pagingAttrs = [theme.pagingAttrs mutableCopy]; - NSMutableDictionary *pagingHighlightedAttrs = [theme.pagingHighlightedAttrs mutableCopy]; - NSMutableDictionary *statusAttrs = [theme.statusAttrs mutableCopy]; + NSMutableDictionary *attrs = theme.attrs.mutableCopy; + NSMutableDictionary *highlightedAttrs = theme.highlightedAttrs.mutableCopy; + NSMutableDictionary *labelAttrs = theme.labelAttrs.mutableCopy; + NSMutableDictionary *labelHighlightedAttrs = theme.labelHighlightedAttrs.mutableCopy; + NSMutableDictionary *commentAttrs = theme.commentAttrs.mutableCopy; + NSMutableDictionary *commentHighlightedAttrs = theme.commentHighlightedAttrs.mutableCopy; + NSMutableDictionary *preeditAttrs = theme.preeditAttrs.mutableCopy; + NSMutableDictionary *preeditHighlightedAttrs = theme.preeditHighlightedAttrs.mutableCopy; + NSMutableDictionary *pagingAttrs = theme.pagingAttrs.mutableCopy; + NSMutableDictionary *pagingHighlightedAttrs = theme.pagingHighlightedAttrs.mutableCopy; + NSMutableDictionary *statusAttrs = theme.statusAttrs.mutableCopy; attrs[NSFontAttributeName] = font; highlightedAttrs[NSFontAttributeName] = font; @@ -2818,23 +3187,54 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditAttrs[NSFontAttributeName] = font; preeditHighlightedAttrs[NSFontAttributeName] = font; pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; + pagingHighlightedAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *zhFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)font, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); - NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)commentFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *zhFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)font, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); + NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)commentFont, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); NSFont *refFont = getTallestFont(@[zhFont, labelFont, zhCommentFont], vertical); - labelAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); - labelHighlightedAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); - labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - labelHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - attrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - highlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - commentAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - commentHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; - preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; - pagingAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): linear ? labelFont : pagingFont}; - statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhCommentFont.verticalFont : zhCommentFont}; + + attrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + highlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + labelAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + labelHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + commentAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + commentHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; + preeditAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? zhFont.verticalFont : zhFont}; + preeditHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? zhFont.verticalFont : zhFont}; + pagingAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: linear ? (vertical ? refFont.verticalFont : refFont) : pagingFont}; + pagingHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: linear ? (vertical ? refFont.verticalFont : refFont) : pagingFont}; + statusAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + @{(NSString *)kCTBaselineReferenceFont: vertical ? zhCommentFont.verticalFont : zhCommentFont}; + + attrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + highlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + labelAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassIdeographicCentered; + labelHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassIdeographicCentered; + commentAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + commentHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + preeditAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + preeditHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + statusAttrs[(NSString *)kCTBaselineClassAttributeName] = + vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; + pagingAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassRoman; + pagingHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassRoman; attrs[NSBaselineOffsetAttributeName] = baseOffset; highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; @@ -2845,8 +3245,16 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditAttrs[NSBaselineOffsetAttributeName] = baseOffset; preeditHighlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; pagingAttrs[NSBaselineOffsetAttributeName] = baseOffset; + pagingHighlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; statusAttrs[NSBaselineOffsetAttributeName] = baseOffset; + attrs[NSKernAttributeName] = @(ceil(lineHeight * 0.05)); + highlightedAttrs[NSKernAttributeName] = @(ceil(lineHeight * 0.05)); + commentAttrs[NSKernAttributeName] = @(ceil(lineHeight * 0.05)); + commentHighlightedAttrs[NSKernAttributeName] = @(ceil(lineHeight * 0.05)); + preeditAttrs[NSKernAttributeName] = @(ceil(fontHeight * 0.05)); + preeditHighlightedAttrs[NSKernAttributeName] = @(ceil(fontHeight * 0.05)); + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; @@ -2854,37 +3262,42 @@ + (void)updateTheme:(SquirrelTheme *)theme labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); labelHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(vertical); pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); + pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(NO); // CHROMATICS refinement - if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - (appear == darkAppear)) <= 0.4) { - backgroundColor = inverseColor(backgroundColor); - borderColor = inverseColor(borderColor); - preeditBackgroundColor = inverseColor(preeditBackgroundColor); - candidateTextColor = inverseColor(candidateTextColor); - highlightedCandidateTextColor = [inverseColor(highlightedCandidateTextColor) highlightWithLevel:highlightedCandidateTextColor.brightnessComponent]; - highlightedCandidateBackColor = [inverseColor(highlightedCandidateBackColor) shadowWithLevel:1 - highlightedCandidateBackColor.brightnessComponent]; - candidateLabelColor = inverseColor(candidateLabelColor); - highlightedCandidateLabelColor = [inverseColor(highlightedCandidateLabelColor) highlightWithLevel:highlightedCandidateLabelColor.brightnessComponent]; - commentTextColor = inverseColor(commentTextColor); - highlightedCommentTextColor = [inverseColor(highlightedCommentTextColor) highlightWithLevel:highlightedCommentTextColor.brightnessComponent]; - textColor = inverseColor(textColor); - highlightedTextColor = [inverseColor(highlightedTextColor) highlightWithLevel:highlightedTextColor.brightnessComponent]; - highlightedBackColor = [inverseColor(highlightedBackColor) shadowWithLevel:1 - highlightedBackColor.brightnessComponent]; - } - - backgroundColor = backgroundColor ? : [NSColor controlBackgroundColor]; - borderColor = borderColor ? : isNative ? [NSColor gridColor] : nil; - preeditBackgroundColor = preeditBackgroundColor ? : isNative ? [NSColor windowBackgroundColor] : nil; - candidateTextColor = candidateTextColor ? : [NSColor controlTextColor]; - highlightedCandidateTextColor = highlightedCandidateTextColor ? : [NSColor selectedMenuItemTextColor]; - highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? [NSColor selectedContentBackgroundColor] : nil; - candidateLabelColor = candidateLabelColor ? : isNative ? [NSColor controlAccentColor] : blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); - highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? [NSColor alternateSelectedControlTextColor] : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); - commentTextColor = commentTextColor ? : [NSColor secondaryLabelColor]; - highlightedCommentTextColor = highlightedCommentTextColor ? : [NSColor alternateSelectedControlTextColor]; - textColor = textColor ? textColor : [NSColor textColor]; - highlightedTextColor = highlightedTextColor ? : [NSColor selectedTextColor]; - highlightedBackColor = highlightedBackColor ? : isNative ? [NSColor selectedTextBackgroundColor] : nil; + translucency = translucency ? : @(0.0); + if (translucency.doubleValue > 0 && (appear == darkAppear ? backgroundColor.luminanceComponent > 0.65 + : backgroundColor.luminanceComponent < 0.55)) { + backgroundColor = [backgroundColor invertLuminanceWithAdjustment:0]; + borderColor = [borderColor invertLuminanceWithAdjustment:0]; + preeditBackgroundColor = [preeditBackgroundColor invertLuminanceWithAdjustment:0]; + candidateTextColor = [candidateTextColor invertLuminanceWithAdjustment:0]; + highlightedCandidateTextColor = [highlightedCandidateTextColor invertLuminanceWithAdjustment:1]; + highlightedCandidateBackColor = [highlightedCandidateBackColor invertLuminanceWithAdjustment:-1]; + candidateLabelColor = [candidateLabelColor invertLuminanceWithAdjustment:0]; + highlightedCandidateLabelColor = [highlightedCandidateLabelColor invertLuminanceWithAdjustment:1]; + commentTextColor = [commentTextColor invertLuminanceWithAdjustment:0]; + highlightedCommentTextColor = [highlightedCommentTextColor invertLuminanceWithAdjustment:1]; + textColor = [textColor invertLuminanceWithAdjustment:0]; + highlightedTextColor = [highlightedTextColor invertLuminanceWithAdjustment:1]; + highlightedBackColor = [highlightedBackColor invertLuminanceWithAdjustment:-1]; + } + + backgroundColor = backgroundColor ? : NSColor.controlBackgroundColor; + borderColor = borderColor ? : isNative ? NSColor.gridColor : nil; + preeditBackgroundColor = preeditBackgroundColor ? : isNative ? NSColor.windowBackgroundColor : nil; + candidateTextColor = candidateTextColor ? : NSColor.controlTextColor; + highlightedCandidateTextColor = highlightedCandidateTextColor ? : NSColor.selectedMenuItemTextColor; + highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? NSColor.selectedContentBackgroundColor : nil; + candidateLabelColor = candidateLabelColor ? : isNative ? NSColor.controlAccentColor : + blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); + highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? + NSColor.alternateSelectedControlTextColor : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); + commentTextColor = commentTextColor ? : NSColor.secondaryLabelColor; + highlightedCommentTextColor = highlightedCommentTextColor ? : NSColor.alternateSelectedControlTextColor; + textColor = textColor ? : NSColor.textColor; + highlightedTextColor = highlightedTextColor ? : NSColor.selectedTextColor; + highlightedBackColor = highlightedBackColor ? : isNative ? NSColor.selectedTextBackgroundColor : nil; attrs[NSForegroundColorAttributeName] = candidateTextColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; @@ -2898,18 +3311,18 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedTextColor; statusAttrs[NSForegroundColorAttributeName] = commentTextColor; - NSSize edgeInset = vertical ? NSMakeSize(MAX(borderHeight.doubleValue, 0), MAX(borderWidth.doubleValue, 0)) : - NSMakeSize(MAX(borderWidth.doubleValue, 0), MAX(borderHeight.doubleValue, 0)); + NSSize edgeInset = vertical ? NSMakeSize(borderHeight.doubleValue, borderWidth.doubleValue) + : NSMakeSize(borderWidth.doubleValue, borderHeight.doubleValue); - [theme setCornerRadius:MIN(cornerRadius.doubleValue, lineHeight / 2) - highlightedCornerRadius:MIN(highlightedCornerRadius.doubleValue, lineHeight / 3) + [theme setCornerRadius:cornerRadius.doubleValue + highlightedCornerRadius:highlightedCornerRadius.doubleValue separatorWidth:separatorWidth edgeInset:edgeInset - linespace:MAX(lineSpacing.doubleValue, 0) - preeditLinespace:MAX(spacing.doubleValue, 0) - alpha:(alpha ? MIN(MAX(alpha.doubleValue, 0.0), 1.0) : 1.0) - translucency:(translucency ? MIN(MAX(translucency.doubleValue, 0.0), 1.0) : 0.0) - lineLength:lineLength.doubleValue > 0 ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 + linespace:lineSpacing.doubleValue + preeditLinespace:spacing.doubleValue + alpha:alpha ? alpha.doubleValue : 1.0 + translucency:translucency.doubleValue + lineLength:lineLength && lineLength.doubleValue > 0 ? fmax(ceil(lineLength.doubleValue), separatorWidth * 5) : 0.0 showPaging:showPaging.boolValue rememberSize:rememberSize.boolValue tabled:tabled diff --git a/input_source.m b/input_source.m index 3c5572ff3..fd8833c79 100644 --- a/input_source.m +++ b/input_source.m @@ -3,22 +3,23 @@ static const char kInstallLocation[] = "/Library/Input Methods/Squirrel.app"; -static CFStringRef kHansInputModeID = +static const CFStringRef kHansInputModeID = CFSTR("im.rime.inputmethod.Squirrel.Hans"); -static CFStringRef kHantInputModeID = +static const CFStringRef kHantInputModeID = CFSTR("im.rime.inputmethod.Squirrel.Hant"); -static CFStringRef kCantInputModeID = +static const CFStringRef kCantInputModeID = CFSTR("im.rime.inputmethod.Squirrel.Cant"); -typedef enum { - HANS_INPUT_MODE = 1 << 0, - HANT_INPUT_MODE = 1 << 1, - CANT_INPUT_MODE = 1 << 2 +typedef enum RimeInputMode : int { + DEFAULT_INPUT_MODE = 1 << 0, + HANS_INPUT_MODE = 1 << 0, + HANT_INPUT_MODE = 1 << 1, + CANT_INPUT_MODE = 1 << 2 } RimeInputMode; void RegisterInputSource(void) { - CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( - NULL, (UInt8 *)kInstallLocation, (CFIndex)strlen(kInstallLocation), false); + CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation + (NULL, (UInt8 *)kInstallLocation, (CFIndex)strlen(kInstallLocation), false); if (installedLocationURL) { TISRegisterInputSource(installedLocationURL); CFRelease(installedLocationURL); @@ -26,18 +27,15 @@ void RegisterInputSource(void) { } } -void ActivateInputSource(int enabled_modes) { +void ActivateInputSource(RimeInputMode modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if ((CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo && - (enabled_modes & HANS_INPUT_MODE) != 0) || - (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo && - (enabled_modes & HANT_INPUT_MODE) != 0) || - (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo && - (enabled_modes & CANT_INPUT_MODE) != 0)) { + if ((!CFStringCompare(sourceID, kHansInputModeID, 0) && (modes & HANS_INPUT_MODE)) || + (!CFStringCompare(sourceID, kHantInputModeID, 0) && (modes & HANT_INPUT_MODE)) || + (!CFStringCompare(sourceID, kCantInputModeID, 0) && (modes & CANT_INPUT_MODE))) { OSStatus enableError = TISEnableInputSource(inputSource); if (enableError) { NSLog(@"Error %d. Failed to enable input mode: %@", enableError, sourceID); @@ -65,9 +63,9 @@ void DeactivateInputSource(void) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo || - CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo || - CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo) { + if (!CFStringCompare(sourceID, kHansInputModeID, 0) || + !CFStringCompare(sourceID, kHantInputModeID, 0) || + !CFStringCompare(sourceID, kCantInputModeID, 0)) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { @@ -83,24 +81,24 @@ void DeactivateInputSource(void) { CFRelease(sourceList); } -int GetEnabledInputModes(void) { - int input_modes = 0; +RimeInputMode GetEnabledInputModes(void) { + RimeInputMode input_modes = 0; CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); - if (CFStringCompare(sourceID, kHansInputModeID, 0) == 0 || - CFStringCompare(sourceID, kHantInputModeID, 0) == 0 || - CFStringCompare(sourceID, kCantInputModeID, 0) == 0) { + if (!CFStringCompare(sourceID, kHansInputModeID, 0) || + !CFStringCompare(sourceID, kHantInputModeID, 0) || + !CFStringCompare(sourceID, kCantInputModeID, 0)) { CFBooleanRef isEnabled = (CFBooleanRef) TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceIsEnabled); if (CFBooleanGetValue(isEnabled)) { - if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo) { + if (!CFStringCompare(sourceID, kHansInputModeID, 0)) { input_modes |= HANS_INPUT_MODE; - } else if (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo) { + } else if (!CFStringCompare(sourceID, kHantInputModeID, 0)) { input_modes |= HANT_INPUT_MODE; - } else if (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo) { + } else if (!CFStringCompare(sourceID, kCantInputModeID, 0)) { input_modes |= CANT_INPUT_MODE; } } diff --git a/macos_keycode.m b/macos_keycode.m index 22a9c7db2..d44547ee7 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -133,5 +133,211 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) { return keychar; } - return XK_VoidSymbol; + switch (keychar) { + case 0x0003: + return XK_KP_Enter; + break; + case 0x0008: + return XK_BackSpace; + break; + case 0x0009: + case 0x0019: + return XK_Tab; + break; + case 0x000a: + return XK_Return; + break; + case 0xF728: + return XK_Delete; + break; + case 0xF700: + return XK_Up; + break; + case 0xF701: + return XK_Down; + break; + case 0xF702: + return XK_Left; + break; + case 0xF703: + return XK_Right; + break; + case 0xF704: + return XK_F1; + break; + case 0xF705: + return XK_F2; + break; + case 0xF706: + return XK_F3; + break; + case 0xF707: + return XK_F4; + break; + case 0xF708: + return XK_F5; + break; + case 0xF709: + return XK_F6; + break; + case 0xF70A: + return XK_F7; + break; + case 0xF70B: + return XK_F8; + break; + case 0xF70C: + return XK_F9; + break; + case 0xF70D: + return XK_F10; + break; + case 0xF70E: + return XK_F11; + break; + case 0xF70F: + return XK_F12; + break; + case 0xF710: + return XK_F13; + break; + case 0xF711: + return XK_F14; + break; + case 0xF712: + return XK_F15; + break; + case 0xF713: + return XK_F16; + break; + case 0xF714: + return XK_F17; + break; + case 0xF715: + return XK_F18; + break; + case 0xF716: + return XK_F19; + break; + case 0xF717: + return XK_F20; + break; + case 0xF718: + return XK_F21; + break; + case 0xF719: + return XK_F22; + break; + case 0xF71A: + return XK_F23; + break; + case 0xF71B: + return XK_F24; + break; + case 0xF71C: + return XK_F25; + break; + case 0xF71D: + return XK_F26; + break; + case 0xF71E: + return XK_F27; + break; + case 0xF71F: + return XK_F28; + break; + case 0xF720: + return XK_F29; + break; + case 0xF721: + return XK_F30; + break; + case 0xF722: + return XK_F31; + break; + case 0xF723: + return XK_F32; + break; + case 0xF724: + return XK_F33; + break; + case 0xF725: + return XK_F34; + break; + case 0xF726: + return XK_F35; + break; + case 0xF72A: + return XK_Begin; + break; + case 0xF72C: + return XK_Page_Up; + break; + case 0xF72D: + return XK_Page_Down; + break; + case 0xF729: + return XK_Home; + break; + case 0xF72B: + return XK_End; + break; + case 0xF732: + return XK_Break; + break; + case 0xF73A: + return XK_Clear; + break; + case 0xF739: + return XK_Num_Lock; + break; + case 0xF73E: + return XK_Delete; + break; + case 0xF742: + return XK_Execute; + break; + case 0xF745: + return XK_Find; + break; + case 0xF746: + return XK_Help; + break; + case 0xF727: + return XK_Insert; + break; + case 0xF735: + return XK_Menu; + break; + case 0xF747: + return XK_Mode_switch; + break; + case 0xF730: + return XK_Pause; + break; + case 0xF738: + return XK_Print; + break; + case 0xF744: + return XK_Redo; + break; + case 0xF72F: + return XK_Scroll_Lock; + break; + case 0xF741: + return XK_Select; + break; + case 0xF734: + return XK_Cancel; + break; + case 0xF731: + return XK_Sys_Req; + break; + case 0xF743: + return XK_Undo; + break; + default: + return XK_VoidSymbol; + break; + } } diff --git a/main.m b/main.m index 6984cda35..ed952c24d 100644 --- a/main.m +++ b/main.m @@ -4,7 +4,7 @@ #import #import -typedef enum { +typedef enum RimeInputMode : int { DEFAULT_INPUT_MODE = 1 << 0, HANS_INPUT_MODE = 1 << 0, HANT_INPUT_MODE = 1 << 1, @@ -12,9 +12,9 @@ } RimeInputMode; void RegisterInputSource(void); -int GetEnabledInputModes(void); +RimeInputMode GetEnabledInputModes(void); void DeactivateInputSource(void); -void ActivateInputSource(RimeInputMode); +void ActivateInputSource(RimeInputMode modes); // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. @@ -22,7 +22,7 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--quit", argv[1])) { - NSString *bundleId = [NSBundle mainBundle].bundleIdentifier; + NSString *bundleId = NSBundle.mainBundle.bundleIdentifier; NSArray *runningSquirrels = [NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId]; for (NSRunningApplication *squirrelApp in runningSquirrels) { @@ -32,7 +32,7 @@ int main(int argc, char *argv[]) { } if (argc > 1 && !strcmp("--reload", argv[1])) { - [[NSDistributedNotificationCenter defaultCenter] + [NSDistributedNotificationCenter.defaultCenter postNotificationName:@"SquirrelReloadNotification" object:nil]; return 0; @@ -41,9 +41,9 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--install", argv[1])) { // register and enable Squirrel RegisterInputSource(); - int input_modes = GetEnabledInputModes(); + RimeInputMode input_modes = GetEnabledInputModes(); DeactivateInputSource(); - ActivateInputSource(input_modes ? input_modes : DEFAULT_INPUT_MODE); + ActivateInputSource(input_modes ? : DEFAULT_INPUT_MODE); return 0; } @@ -59,7 +59,7 @@ int main(int argc, char *argv[]) { } if (argc > 1 && !strcmp("--sync", argv[1])) { - [[NSDistributedNotificationCenter defaultCenter] + [NSDistributedNotificationCenter.defaultCenter postNotificationName:@"SquirrelSyncNotification" object:nil]; return 0; @@ -67,34 +67,38 @@ int main(int argc, char *argv[]) { @autoreleasepool { // find the bundle identifier and then initialize the input method server - NSBundle *main = [NSBundle mainBundle]; + NSBundle *main = NSBundle.mainBundle; IMKServer *server __unused = [[IMKServer alloc] initWithName:kConnectionName bundleIdentifier:main.bundleIdentifier]; // load the bundle explicitly because in this case the input method is a // background only application - [main loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication] topLevelObjects:nil]; + [main loadNibNamed:@"MainMenu" + owner:NSApplication.sharedApplication + topLevelObjects:nil]; // opencc will be configured with relative dictionary paths - [[NSFileManager defaultManager] + [NSFileManager.defaultManager changeCurrentDirectoryPath:main.sharedSupportPath]; if (NSApp.squirrelAppDelegate.problematicLaunchDetected) { NSLog(@"Problematic launch detected!"); NSArray *args = @[@"-v", NSLocalizedString(@"say_voice", nil), NSLocalizedString(@"problematic_launch", nil)]; - [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:@"/usr/bin/say"] - arguments:args error:nil terminationHandler:nil]; + [NSTask launchedTaskWithExecutableURL:[NSURL fileURLWithPath:@"/usr/bin/say" isDirectory:NO] + arguments:args + error:nil + terminationHandler:nil]; } else { [NSApp.squirrelAppDelegate setupRime]; - [NSApp.squirrelAppDelegate startRimeWithFullCheck:NO]; + [NSApp.squirrelAppDelegate startRimeWithFullCheck:false]; [NSApp.squirrelAppDelegate loadSettings]; NSLog(@"Squirrel reporting!"); } // finally run everything - [[NSApplication sharedApplication] run]; + [NSApplication.sharedApplication run]; NSLog(@"Squirrel is quitting..."); rime_get_api()->finalize(); From 469f64779ee1fc183452794bb1c200ecf4b536b6 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 27 Jan 2024 11:58:54 +0100 Subject: [PATCH 158/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 8b678a7ad..af9fef8ca 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 8b678a7ad7553e8e203bbfa784f6fd78fc02a8a1 +Subproject commit af9fef8ca4ebf616c0413a8f57b8de25cfdad062 From e6ae4cf67944c3fb7114029d27cc44b42e93e61b Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 8 Feb 2024 22:44:10 +0100 Subject: [PATCH 159/164] update & cleanup --- SquirrelApplicationDelegate.h | 22 +- SquirrelApplicationDelegate.m | 188 ++- SquirrelConfig.h | 5 +- SquirrelConfig.m | 121 +- SquirrelInputController.h | 8 +- SquirrelInputController.m | 410 ++++--- SquirrelPanel.h | 18 +- SquirrelPanel.m | 2116 ++++++++++++++++++--------------- input_source.m | 19 +- macos_keycode.h | 236 +--- macos_keycode.m | 476 +++----- main.m | 9 +- 12 files changed, 1782 insertions(+), 1846 deletions(-) diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index b3c8acb6d..08e101b2d 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -1,18 +1,25 @@ #import +#import @class SquirrelConfig; @class SquirrelPanel; @class SquirrelOptionSwitcher; +typedef enum { + kShowNotificationsNever = 0, + kShowNotificationsWhenAppropriate = 1, + kShowNotificationsAlways = 2 +} SquirrelNotificationPolicy; + // Note: the SquirrelApplicationDelegate is instantiated automatically as an outlet of NSApp's instance @interface SquirrelApplicationDelegate : NSObject -@property(nonatomic, copy) IBOutlet NSMenu *menu; -@property(nonatomic, strong) IBOutlet SquirrelPanel *panel; -@property(nonatomic, strong) IBOutlet id updater; +@property(nonatomic, weak) IBOutlet NSMenu *menu; +@property(nonatomic, weak) IBOutlet SquirrelPanel *panel; +@property(nonatomic, weak) IBOutlet id updater; @property(nonatomic, readonly, strong) SquirrelConfig *config; -@property(nonatomic, readonly) BOOL enableNotifications; +@property(nonatomic, readonly) SquirrelNotificationPolicy showNotifications; - (IBAction)deploy:(id)sender; - (IBAction)syncUserData:(id)sender; @@ -21,9 +28,10 @@ - (IBAction)openLogFolder:(id)sender; - (void)setupRime; -- (void)startRimeWithFullCheck:(bool)fullCheck; +- (void)startRimeWithFullCheck:(BOOL)fullCheck; - (void)loadSettings; -- (void)loadSchemaSpecificSettings:(NSString *)schemaId; +- (void)loadSchemaSpecificSettings:(NSString *)schemaId + withRimeSession:(RimeSessionId)sessionId; - (void)loadSchemaSpecificLabels:(NSString *)schemaId; @property(nonatomic, readonly) BOOL problematicLaunchDetected; @@ -37,4 +45,4 @@ @end // also used in main.m -extern void show_message(const char *msg_text, const char *msg_id); +extern void show_notification(const char *msg_text); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 99a41a1ef..721491f77 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -1,10 +1,10 @@ #import "SquirrelApplicationDelegate.h" -#import #import "SquirrelConfig.h" #import "SquirrelPanel.h" +#import -static NSString *kRimeWikiURL = @"https://github.com/rime/home/wiki"; +static NSString *const kRimeWikiURL = @"https://github.com/rime/home/wiki"; @implementation SquirrelApplicationDelegate @@ -36,11 +36,41 @@ - (IBAction)openLogFolder:(id)sender { inFileViewerRootedAtPath:tmpDir]; } -void show_message(const char *msg_text, const char *msg_id) { - @autoreleasepool { +void show_notification(const char *msg_text) { + if (@available(macOS 10.14, *)) { + UNUserNotificationCenter *center = UNUserNotificationCenter.currentNotificationCenter; + [center requestAuthorizationWithOptions:UNAuthorizationOptionAlert|UNAuthorizationOptionProvisional + completionHandler:^(BOOL granted, NSError *error) { + if (error) { + NSLog(@"User notification authorization error: %@", error.debugDescription); + } + }]; + [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings) { + if ((settings.authorizationStatus == UNAuthorizationStatusAuthorized || + settings.authorizationStatus == UNAuthorizationStatusProvisional) && + (settings.alertSetting == UNNotificationSettingEnabled)) { + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + content.title = NSLocalizedString(@"Squirrel", nil); + content.subtitle = NSLocalizedString(@(msg_text), nil); + if (@available(macOS 12.0, *)) { + content.interruptionLevel = UNNotificationInterruptionLevelActive; + } + UNNotificationRequest *request = + [UNNotificationRequest requestWithIdentifier:@"SquirrelNotification" + content:content + trigger:nil]; + [center addNotificationRequest:request + withCompletionHandler:^(NSError *error) { + if (error) { + NSLog(@"User notification request error: %@", error.debugDescription); + } + }]; + } + }]; + } else { NSUserNotification *notification = [[NSUserNotification alloc] init]; [notification setTitle:NSLocalizedString(@"Squirrel", nil)]; - [notification setTitle:NSLocalizedString(@(msg_text), nil)]; + [notification setSubtitle:NSLocalizedString(@(msg_text), nil)]; NSUserNotificationCenter *notificationCenter = NSUserNotificationCenter.defaultUserNotificationCenter; @@ -49,36 +79,34 @@ void show_message(const char *msg_text, const char *msg_id) { } } -static void show_status_message(const char *msg_text_long, const char *msg_text_short, const char *msg_id) { - SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; - - if (panel) { - NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; - NSString *msgShort = msg_text_short ? @(msg_text_short) : nil; - [panel updateStatusLong:msgLong statusShort:msgShort]; - } +static void show_status(const char *msg_text_long, const char *msg_text_short) { + NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; + NSString *msgShort = msg_text_short ? @(msg_text_short) : + (msgLong ? [msgLong substringWithRange: + [msgLong rangeOfComposedCharacterSequenceAtIndex:0]] : nil); + [NSApp.squirrelAppDelegate.panel updateStatusLong:msgLong statusShort:msgShort]; } static void notification_handler(void *context_object, RimeSessionId session_id, const char *message_type, const char *message_value) { if (!strcmp(message_type, "deploy")) { if (!strcmp(message_value, "start")) { - show_message("deploy_start", message_type); + show_notification("deploy_start"); } else if (!strcmp(message_value, "success")) { - show_message("deploy_success", message_type); + show_notification("deploy_success"); } else if (!strcmp(message_value, "failure")) { - show_message("deploy_failure", message_type); + show_notification("deploy_failure"); } return; } - id app_delegate = (__bridge id)context_object; + SquirrelApplicationDelegate *app_delegate = (__bridge id)context_object; // schema change if (!strcmp(message_type, "schema") && - app_delegate && [app_delegate enableNotifications]) { + app_delegate.showNotifications != kShowNotificationsNever) { const char *schema_name = strchr(message_value, '/'); if (schema_name) { ++schema_name; - show_status_message(schema_name, schema_name, message_type); + show_status(schema_name, schema_name); } return; } @@ -86,22 +114,23 @@ static void notification_handler(void *context_object, RimeSessionId session_id, if (!strcmp(message_type, "option") && app_delegate) { Bool state = message_value[0] != '!'; const char *option_name = message_value + !state; - if ([[app_delegate panel].optionSwitcher containsOption:@(option_name)]) { - if ([[app_delegate panel].optionSwitcher updateGroupState:@(message_value) - ofOption:@(option_name)]) { + if ([app_delegate.panel.optionSwitcher containsOption:@(option_name)]) { + if ([app_delegate.panel.optionSwitcher updateGroupState:@(message_value) + ofOption:@(option_name)]) { NSString *schemaId = [app_delegate panel].optionSwitcher.schemaId; [app_delegate loadSchemaSpecificLabels:schemaId]; - [app_delegate loadSchemaSpecificSettings:schemaId]; + [app_delegate loadSchemaSpecificSettings:schemaId + withRimeSession:session_id]; } } - if ([app_delegate enableNotifications]) { + if (app_delegate.showNotifications != kShowNotificationsNever) { RimeStringSlice state_label_long = rime_get_api()-> get_state_label_abbreviated(session_id, option_name, state, False); RimeStringSlice state_label_short = rime_get_api()-> get_state_label_abbreviated(session_id, option_name, state, True); if (state_label_long.str || state_label_short.str) { const char *short_message = state_label_short.length < strlen(state_label_short.str) ? NULL : state_label_short.str; - show_status_message(state_label_long.str, short_message, message_type); + show_status(state_label_long.str, short_message); } } } @@ -118,24 +147,23 @@ - (void)setupRime { NSLog(@"Error creating user data directory: %@", userDataDir); } } - rime_get_api()->set_notification_handler(notification_handler, (__bridge void *)(self)); + rime_get_api()->set_notification_handler(notification_handler, (__bridge void *)self); RIME_STRUCT(RimeTraits, squirrel_traits); squirrel_traits.shared_data_dir = NSBundle.mainBundle.sharedSupportPath.UTF8String; squirrel_traits.user_data_dir = userDataDir.UTF8String; squirrel_traits.distribution_code_name = "Squirrel"; - squirrel_traits.distribution_name = "鼠鬚管"; - squirrel_traits.distribution_version = - [NSBundle.mainBundle.infoDictionary[@"CFBundleVersion"] UTF8String]; + squirrel_traits.distribution_name = NSLocalizedString(@"Squirrel", nil).UTF8String; + squirrel_traits.distribution_version = [[NSBundle.mainBundle objectForInfoDictionaryKey: + (NSString *)kCFBundleVersionKey] UTF8String]; squirrel_traits.app_name = "rime.squirrel"; rime_get_api()->setup(&squirrel_traits); } -- (void)startRimeWithFullCheck:(bool)fullCheck -{ +- (void)startRimeWithFullCheck:(BOOL)fullCheck { NSLog(@"Initializing la rime..."); rime_get_api()->initialize(NULL); // check for configuration updates - if (rime_get_api()->start_maintenance(fullCheck)) { + if (rime_get_api()->start_maintenance((Bool)fullCheck)) { // update squirrel config rime_get_api()->deploy_config_file("squirrel.yaml", "config_version"); } @@ -146,30 +174,95 @@ - (void)shutdownRime { rime_get_api()->finalize(); } +NSArray * getScriptOptionsForSchema(SquirrelConfig *schema) { + NSUInteger numSwitches = [schema getListSize:@"switches"]; + if (numSwitches == 0) { + return nil; + } + for (NSUInteger i = 0; i < numSwitches; ++i) { + NSString *name = [schema getString:[NSString stringWithFormat: + @"switches/@%lu/name", i]]; + if (name) { + if ([name isEqualToString:@"simplification"] || + [name isEqualToString:@"traditional"]) { + return @[name]; + } + } else { + NSArray *options = [schema getList:[NSString stringWithFormat: + @"switches/@%lu/options", i]]; + if ([options containsObject:@"simplification"] || + [options containsObject:@"traditional"]) { + return options; + } + } + } + return nil; +} + +SquirrelOptionSwitcher * updateOptionSwitcher(SquirrelOptionSwitcher *optionSwitcher, + RimeSessionId sessionId) { + NSMutableDictionary *switcher = optionSwitcher.mutableSwitcher; + NSSet *prevStates = [NSSet setWithArray:optionSwitcher.optionStates]; + for (NSString *state in prevStates) { + NSString *updatedState; + NSArray *optionGroup = [optionSwitcher.switcher allKeysForObject:state]; + for (NSString *option in optionGroup) { + if (rime_get_api()->get_option(sessionId, option.UTF8String)) { + updatedState = option; + break; + } + } + updatedState = updatedState ? : [@"!" stringByAppendingString:optionGroup[0]]; + if (![updatedState isEqualToString:state]) { + for (NSString *option in optionGroup) { + switcher[option] = updatedState; + } + } + } + [optionSwitcher updateSwitcher:switcher]; + return optionSwitcher; +} + - (void)loadSettings { _config = [[SquirrelConfig alloc] init]; if (![_config openBaseConfig]) { return; } - _enableNotifications = - ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; + NSString *showNotificationsWhen = [_config getString:@"show_notifications_when"]; + if ([showNotificationsWhen isEqualToString:@"never"]) { + _showNotifications = kShowNotificationsNever; + } else if ([showNotificationsWhen isEqualToString:@"appropriate"]) { + _showNotifications = kShowNotificationsWhenAppropriate; + } else { + _showNotifications = kShowNotificationsAlways; + } [self.panel loadConfig:_config forAppearance:defaultAppear]; [self.panel loadConfig:_config forAppearance:darkAppear]; } -- (void)loadSchemaSpecificSettings:(NSString *)schemaId { - if (schemaId.length == 0 || [schemaId characterAtIndex:0] == '.') { +- (void)loadSchemaSpecificSettings:(NSString *)schemaId + withRimeSession:(RimeSessionId)sessionId { + if (schemaId.length == 0 || [schemaId hasPrefix:@"."]) { return; } + // update the list of switchers that change styles and color-themes + SquirrelOptionSwitcher *optionSwitcher; SquirrelConfig *schema = [[SquirrelConfig alloc] init]; - if ([schema openWithSchemaId:schemaId baseConfig:self.config] && - [schema hasSection:@"style"]) { - [self.panel loadConfig:schema forAppearance:defaultAppear]; - [self.panel loadConfig:schema forAppearance:darkAppear]; - } else { - [self.panel loadConfig:self.config forAppearance:defaultAppear]; - [self.panel loadConfig:self.config forAppearance:darkAppear]; + if ([schema openWithSchemaId:schemaId baseConfig:self.config]) { + self.panel.scriptOptions = getScriptOptionsForSchema(schema); + if ([schema hasSection:@"style"]) { + optionSwitcher = [schema getOptionSwitcher]; + self.panel.optionSwitcher = updateOptionSwitcher(optionSwitcher, sessionId); + [self.panel loadConfig:schema forAppearance:defaultAppear]; + [self.panel loadConfig:schema forAppearance:darkAppear]; + } else { + self.panel.optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:schemaId + switcher:@{} + optionGroups:@{}]; + [self.panel loadConfig:self.config forAppearance:defaultAppear]; + [self.panel loadConfig:self.config forAppearance:darkAppear]; + } } [schema close]; } @@ -177,7 +270,7 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { - (void)loadSchemaSpecificLabels:(NSString *)schemaId { SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; [defaultConfig openWithConfigId:@"default"]; - if (schemaId.length == 0 || [schemaId characterAtIndex:0] == '.') { + if (schemaId.length == 0 || [schemaId hasPrefix:@"."]) { [self.panel loadLabelConfig:defaultConfig directUpdate:YES]; [defaultConfig close]; return; @@ -205,7 +298,7 @@ - (BOOL)problematicLaunchDetected { if (archive) { NSDate *previousLaunch = [NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class fromData:archive error:nil]; - if (previousLaunch && previousLaunch.timeIntervalSinceNow >= -2) { + if (previousLaunch.timeIntervalSinceNow >= -2) { detected = YES; } } @@ -234,6 +327,7 @@ - (void)rimeNeedsSync:(NSNotification *)aNotification { - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { NSLog(@"Squirrel is quitting."); + [_config close]; rime_get_api()->cleanup_all_sessions(); return NSTerminateNow; } @@ -263,9 +357,7 @@ - (void)awakeFromNib { - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; [NSDistributedNotificationCenter.defaultCenter removeObserver:self]; - if (_panel) { - [_panel hide]; - } + [_panel hide]; } @end //SquirrelApplicationDelegate diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 307f4a827..97e273907 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -30,8 +30,8 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; @interface SquirrelConfig : NSObject @property(nonatomic, readonly) BOOL isOpen; -@property(nonatomic, copy) NSString *colorSpace; -@property(nonatomic, readonly) NSString *schemaId; +@property(nonatomic, strong) NSString *colorSpace; +@property(nonatomic, strong, readonly) NSString *schemaId; - (BOOL)openBaseConfig; - (BOOL)openWithSchemaId:(NSString *)schemaId @@ -59,6 +59,7 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; // file path (absolute or relative to ~/Library/Rime) - (NSImage *)getImage:(NSString *)option; +- (NSUInteger)getListSize:(NSString *)option; - (NSArray *)getList:(NSString *)option; - (SquirrelOptionSwitcher *)getOptionSwitcher; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index fc1730076..1c7c30639 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -2,18 +2,12 @@ #import -@implementation SquirrelOptionSwitcher { - NSString *_schemaId; - NSDictionary *_switcher; - NSDictionary *> *_optionGroups; - NSArray *_optionNames; -} +@implementation SquirrelOptionSwitcher - (instancetype)initWithSchemaId:(NSString *)schemaId switcher:(NSDictionary *)switcher optionGroups:(NSDictionary *> *)optionGroups { - self = [super init]; - if (self) { + if (self = [super init]) { _schemaId = schemaId; _switcher = switcher; _optionGroups = optionGroups; @@ -22,28 +16,16 @@ - (instancetype)initWithSchemaId:(NSString *)schemaId return self; } -- (NSString *)schemaId { - return _schemaId; -} - -- (NSArray *)optionNames { - return _optionNames; -} - - (NSArray *)optionStates { return _switcher.allValues; } -- (NSDictionary *)switcher { - return _switcher; -} - - (BOOL)updateSwitcher:(NSDictionary *)switcher { if (switcher.count != _switcher.count) { return NO; } - NSMutableDictionary *updatedSwitcher = - [[NSMutableDictionary alloc] initWithCapacity:switcher.count]; + NSMutableDictionary *updatedSwitcher = [[NSMutableDictionary alloc] + initWithCapacity:switcher.count]; for (NSString *option in _optionNames) { if (switcher[option] == nil) { return NO; @@ -56,11 +38,11 @@ - (BOOL)updateSwitcher:(NSDictionary *)switcher { - (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName { - NSArray *optionGroup = _optionGroups[optionName]; + NSArray *optionGroup = _optionGroups[optionName]; if (!optionGroup || ![optionGroup containsObject:optionState]) { return NO; } - NSMutableDictionary *updatedSwitcher = [_switcher mutableCopy]; + NSMutableDictionary *updatedSwitcher = [_switcher mutableCopy]; for (NSString *option in optionGroup) { updatedSwitcher[option] = optionState; } @@ -76,33 +58,23 @@ - (BOOL)containsOption:(NSString *)optionName { return [_switcher mutableCopy]; } -@end +@end // SquirrelOptionSwitcher + @implementation SquirrelConfig { NSCache *_cache; RimeConfig _config; - NSString *_schemaId; SquirrelConfig *_baseConfig; - BOOL _isOpen; } - (instancetype)init { - self = [super init]; - if (self) { + if (self = [super init]) { _cache = [[NSCache alloc] init]; + _colorSpace = @"srgb"; } - self.colorSpace = @"srgb"; return self; } -- (BOOL)isOpen { - return _isOpen; -} - -- (NSString *)schemaId { - return _schemaId; -} - - (BOOL)openBaseConfig { [self close]; _isOpen = !!rime_get_api()->config_open("squirrel", &_config); @@ -140,6 +112,10 @@ - (void)close { } } +- (void)dealloc { + [self close]; +} + - (BOOL)hasSection:(NSString *)section { if (_isOpen) { RimeConfigIterator iterator = {0}; @@ -170,40 +146,43 @@ - (double)getDouble:(NSString *)option } - (NSNumber *)getOptionalBool:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; + NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(BOOL) forKey:option]; if (cachedValue) { - return cachedValue; + return cachedValue; } Bool value; if (_isOpen && rime_get_api()->config_get_bool(&_config, option.UTF8String, &value)) { - [_cache setObject:@(!!value) forKey:option]; - return @(!!value); + NSNumber *number = [NSNumber numberWithBool:(BOOL)value]; + [_cache setObject:number forKey:option]; + return number; } return [_baseConfig getOptionalBool:option]; } - (NSNumber *)getOptionalInt:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; + NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(int) forKey:option]; if (cachedValue) { return cachedValue; } int value; if (_isOpen && rime_get_api()->config_get_int(&_config, option.UTF8String, &value)) { - [_cache setObject:@(value) forKey:option]; - return @(value); + NSNumber *number = [NSNumber numberWithInt:value]; + [_cache setObject:number forKey:option]; + return number; } return [_baseConfig getOptionalInt:option]; } - (NSNumber *)getOptionalDouble:(NSString *)option { - NSNumber *cachedValue = [self cachedValueOfClass:NSNumber.class forKey:option]; + NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(double) forKey:option]; if (cachedValue) { return cachedValue; } double value; if (_isOpen && rime_get_api()->config_get_double(&_config, option.UTF8String, &value)) { - [_cache setObject:@(value) forKey:option]; - return @(value); + NSNumber *number = [NSNumber numberWithDouble:value]; + [_cache setObject:number forKey:option]; + return number; } return [_baseConfig getOptionalDouble:option]; } @@ -211,7 +190,7 @@ - (NSNumber *)getOptionalDouble:(NSString *)option { - (NSNumber *)getOptionalDouble:(NSString *)option applyConstraint:(double(*)(double param))func { NSNumber *value = [self getOptionalDouble:option]; - return value ? @(func(value.doubleValue)) : nil; + return value ? [NSNumber numberWithDouble:func(value.doubleValue)] : nil; } - (NSString *)getString:(NSString *)option { @@ -222,7 +201,8 @@ - (NSString *)getString:(NSString *)option { const char *value = _isOpen ? rime_get_api()->config_get_cstring(&_config, option.UTF8String) : NULL; if (value) { - NSString *string = [@(value) stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet]; + NSString *string = [@(value) stringByTrimmingCharactersInSet: + NSCharacterSet.whitespaceCharacterSet]; [_cache setObject:string forKey:option]; return string; } @@ -255,12 +235,16 @@ - (NSImage *)getImage:(NSString *)option { return [_baseConfig getImage:option]; } +- (NSUInteger)getListSize:(NSString *)option { + return rime_get_api()->config_list_size(&_config, option.UTF8String); +} + - (NSArray *)getList:(NSString *)option { RimeConfigIterator iterator; if (!rime_get_api()->config_begin_list(&iterator, &_config, option.UTF8String)) { return nil; } - NSMutableArray *strList = [[NSMutableArray alloc] init]; + NSMutableArray *strList = [[NSMutableArray alloc] init]; while (rime_get_api()->config_next(&iterator)) { [strList addObject:[self getString:@(iterator.path)]]; } @@ -273,8 +257,8 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { if (!rime_get_api()->config_begin_list(&switchIter, &_config, "switches")) { return nil; } - NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; - NSMutableDictionary *> *optionGroups = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *optionGroups = [[NSMutableDictionary alloc] init]; while (rime_get_api()->config_next(&switchIter)) { int reset = [self getInt:[@(switchIter.path) stringByAppendingString:@"/reset"]]; NSString *name = [self getString:[@(switchIter.path) stringByAppendingString:@"/name"]]; @@ -285,10 +269,13 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { optionGroups[name] = @[name]; } } else { + RimeConfigIterator optionIter; + if (!rime_get_api()->config_begin_list(&optionIter, &_config, + [@(switchIter.path) stringByAppendingString:@"/options"].UTF8String)) { + continue; + } NSMutableArray *optionGroup = [[NSMutableArray alloc] init]; BOOL hasStyleSection = NO; - RimeConfigIterator optionIter; - rime_get_api()->config_begin_list(&optionIter, &_config, [@(switchIter.path) stringByAppendingString:@"/options"].UTF8String); while (rime_get_api()->config_next(&optionIter)) { NSString *option = [self getString:@(optionIter.path)]; [optionGroup addObject:option]; @@ -318,11 +305,15 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { } while (rime_get_api()->config_next(&iterator)) { //NSLog(@"DEBUG option[%d]: %s (%s)", iterator.index, iterator.key, iterator.path); - BOOL value = [self getBool:@(iterator.path)]; - appOptions[@(iterator.key)] = @(value); + NSNumber *value = [self getOptionalBool:@(iterator.path)] ? : + [self getOptionalInt:@(iterator.path)] ? : + [self getOptionalDouble:@(iterator.path)]; + if (value) { + appOptions[@(iterator.key)] = value; + } } rime_get_api()->config_end(&iterator); - return appOptions; + return appOptions.count > 0 ? appOptions : nil; } #pragma mark - Private methods @@ -330,7 +321,17 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { - (id)cachedValueOfClass:(Class)aClass forKey:(NSString *)key { id value = [_cache objectForKey:key]; - if (value && [value isMemberOfClass:aClass]) { + if ([value isMemberOfClass:aClass]) { + return value; + } + return nil; +} + +- (NSNumber *)cachedValueOfObjCType:(const char *)type + forKey:(NSString *)key { + id value = [_cache objectForKey:key]; + if ([value isMemberOfClass:NSNumber.class] && + !strcmp([value objCType], type)) { return value; } return nil; @@ -377,4 +378,4 @@ - (NSImage *)imageFromFile:(NSString *)filePath { return nil; } -@end +@end // SquirrelConfig diff --git a/SquirrelInputController.h b/SquirrelInputController.h index ee11f5621..8cd1dbc17 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -1,15 +1,13 @@ #import #import -@interface SquirrelInputController : IMKInputController - typedef enum { kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape) kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A') kDELETE = 3 // only accepts indices in digits (int 1) } rimeAction; -typedef enum rimeIndex : NSUInteger { +typedef NS_ENUM(NSUInteger, rimeIndex) { // 0 ... 9 are ordinal digits, used as (int) index // 0x21 ... 0x7e are ASCII chars (as selection keys) // other rime keycodes (as function keys), for paging etc. @@ -20,7 +18,9 @@ typedef enum rimeIndex : NSUInteger { kPageDown = 0xff56, // XK_Page_Down kEnd = 0xff57, // XK_End kVoidSymbol = 0xffffff // XK_VoidSymbol -} rimeIndex; +}; + +@interface SquirrelInputController : IMKInputController - (void)perform:(rimeAction)action onIndex:(rimeIndex)index; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 2fcdbebdb..c8c85a521 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -6,27 +6,20 @@ #import "macos_keycode.h" #import #import +#import #import -#import - -@interface SquirrelInputController (Private) -- (void)createSession; -- (void)destroySession; -- (void)rimeConsumeCommittedText; -- (void)updateStyleOptions; -- (void)rimeUpdate; -@end const int N_KEY_ROLL_OVER = 50; +static NSString *const kFullWidthSpace = @" "; @implementation SquirrelInputController { NSMutableAttributedString *_preeditString; - NSMutableString *_originalString; - NSMutableString *_composedString; + NSString *_originalString; + NSString *_composedString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; - NSEventModifierFlags _lastModifier; + NSEventModifierFlags _lastModifiers; uint _lastEventCount; RimeSessionId _session; NSString *_schemaId; @@ -35,8 +28,9 @@ @implementation SquirrelInputController { BOOL _showingSwitcherMenu; BOOL _goodOldCapsLock; // app-specific bug fix - BOOL _inlinePlaceHolder; + BOOL _inlinePlaceholder; BOOL _panellessCommitFix; + int _inlineOffset; // for chord-typing int _chordKeyCodes[N_KEY_ROLL_OVER]; int _chordModifiers[N_KEY_ROLL_OVER]; @@ -66,89 +60,103 @@ - (BOOL)handleEvent:(NSEvent *)event } } NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; - int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); + int rime_modifiers = get_rime_modifiers(modifiers); + ushort keyCode = (ushort)CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); switch (event.type) { case NSEventTypeFlagsChanged: { - if (_lastModifier == modifiers) { + if (_lastModifiers == modifiers) { handled = YES; break; } - //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; - ushort keyCode = (ushort)CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); - int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); + int rime_keycode = get_rime_keycode(keyCode, 0, false, false); uint eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventLeftMouseDown) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventRightMouseDown) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); - _lastModifier = modifiers; + _lastModifiers = modifiers; switch (keyCode) { case kVK_CapsLock: if (!_goodOldCapsLock) { set_CapsLock_LED_state(false); - Bool caps_lock_on = rime_get_api()->get_option(_session, "caps_lock_on"); - rime_modifiers = caps_lock_on ? rime_modifiers | kLockMask : rime_modifiers & ~kLockMask; - rime_get_api()->set_option(_session, "caps_lock_on", !caps_lock_on); + Bool ascii_mode = rime_get_api()->get_option(_session, "ascii_mode"); + rime_modifiers = ascii_mode ? rime_modifiers | kLockMask : rime_modifiers & ~kLockMask; } else { rime_modifiers ^= kLockMask; } - [self processKey:rime_keycode modifiers:rime_modifiers]; + handled = [self processKey:rime_keycode modifiers:rime_modifiers]; break; case kVK_Shift: case kVK_RightShift: release_mask = modifiers & NSEventModifierFlagShift ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + handled = [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Control: case kVK_RightControl: release_mask = modifiers & NSEventModifierFlagControl ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + handled = [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Option: case kVK_RightOption: release_mask = modifiers & NSEventModifierFlagOption ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + handled = [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Function: release_mask = modifiers & NSEventModifierFlagFunction ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + handled = [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; case kVK_Command: case kVK_RightCommand: release_mask = modifiers & NSEventModifierFlagCommand ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); - [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; + handled = [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; break; } - [self rimeUpdate]; + if (NSApp.squirrelAppDelegate.panel.statusMessage || handled) { + [self rimeUpdate]; + handled = YES; + } _lastEventCount = eventCount; } break; case NSEventTypeKeyDown: { - ushort keyCode = event.keyCode; - NSString *keyChars = ((modifiers & NSEventModifierFlagShift) && !(modifiers & NSEventModifierFlagControl) && - !(modifiers & NSEventModifierFlagOption)) ? event.characters : event.charactersIgnoringModifiers; + NSString *keyChars = ((modifiers & NSEventModifierFlagShift) && + !(modifiers & NSEventModifierFlagControl) && + !(modifiers & NSEventModifierFlagOption)) ? + event.characters : event.charactersIgnoringModifiers; //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents - int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], - modifiers & NSEventModifierFlagShift, - modifiers & NSEventModifierFlagCapsLock); - if (rime_keycode) { + int rime_keycode = get_rime_keycode(keyCode, [keyChars characterAtIndex:0], + (bool)(modifiers & NSEventModifierFlagShift), + (bool)(modifiers & NSEventModifierFlagCapsLock)); + if (rime_keycode != XK_VoidSymbol) { // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) - if ((keyCode <= 0xff && keyCode >= 0x60) || keyCode == 0x50 || keyCode == 0x4f || - keyCode == 0x47 || keyCode == 0x40) { + if ((keyCode <= 0xff && keyCode >= 0x60) || keyCode == 0x50 || + keyCode == 0x4f || keyCode == 0x47 || keyCode == 0x40) { rime_modifiers ^= kHyperMask; } - handled = [self processKey:rime_keycode modifiers:rime_modifiers]; - [self rimeUpdate]; + if ((handled = [self processKey:rime_keycode modifiers:rime_modifiers])) { + [self rimeUpdate]; + } else if (_panellessCommitFix && self.client.markedRange.length > 0) { + if (rime_keycode == XK_Delete || (rime_keycode >= XK_Home && rime_keycode <= XK_KP_Delete) || + (rime_keycode >= XK_BackSpace && rime_keycode <= XK_Escape)) { + [self showPlaceholder:@""]; + } else if (!(modifiers & (NSEventModifierFlagControl | NSEventModifierFlagCommand)) && + event.characters.length > 0) { + [self showPlaceholder:nil]; + [self.client insertText:event.characters + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + return YES; + } + } } } break; default: @@ -159,7 +167,8 @@ - (BOOL)handleEvent:(NSEvent *)event } void set_CapsLock_LED_state(bool target_state) { - io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass)); + io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching(kIOHIDSystemClass)); io_connect_t ioConnect = 0; IOServiceOpen(ioService, mach_task_self_, kIOHIDParamConnectType, &ioConnect); bool current_state = false; @@ -173,12 +182,12 @@ void set_CapsLock_LED_state(bool target_state) { - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers { // with linear candidate list, arrow keys may behave differently. - Bool is_linear = NSApp.squirrelAppDelegate.panel.linear; + Bool is_linear = (Bool)NSApp.squirrelAppDelegate.panel.linear; if (is_linear != rime_get_api()->get_option(_session, "_linear")) { rime_get_api()->set_option(_session, "_linear", is_linear); } // with vertical text, arrow keys may behave differently. - Bool is_vertical = NSApp.squirrelAppDelegate.panel.vertical; + Bool is_vertical = (Bool)NSApp.squirrelAppDelegate.panel.vertical; if (is_vertical != rime_get_api()->get_option(_session, "_vertical")) { rime_get_api()->set_option(_session, "_vertical", is_vertical); } @@ -190,22 +199,23 @@ - (BOOL)processKey:(int)rime_keycode if (!handled) { BOOL isVimBackInCommandMode = rime_keycode == XK_Escape || - ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || - rime_keycode == XK_C || rime_keycode == XK_bracketleft)); + ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || + rime_keycode == XK_C || rime_keycode == XK_bracketleft)); if (isVimBackInCommandMode && rime_get_api()->get_option(_session, "vim_mode") && !rime_get_api()->get_option(_session, "ascii_mode")) { + [self cancelComposition]; rime_get_api()->set_option(_session, "ascii_mode", True); // NSLog(@"turned Chinese mode off in vim-like editor's command mode"); + return YES; } } // Simulate key-ups for every interesting key-down for chord-typing. if (handled) { - BOOL is_chording_key = - (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || - rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + BOOL is_chording_key = (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || + rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || + rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; if (is_chording_key && rime_get_api()->get_option(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; } else if ((rime_modifiers & kReleaseMask) == 0) { @@ -277,13 +287,13 @@ - (void)updateChord:(int)keycode _chordModifiers[_chordKeyCount] = modifiers; ++_chordKeyCount; // reset timer - if (_chordTimer && _chordTimer.valid) { + if (_chordTimer.valid) { [_chordTimer invalidate]; } _chordDuration = 0.1; NSNumber *duration = [NSApp.squirrelAppDelegate.config getOptionalDouble:@"chord_duration"]; - if (duration && duration.doubleValue > 0) { + if (duration.doubleValue > 0) { _chordDuration = duration.doubleValue; } _chordTimer = [NSTimer scheduledTimerWithTimeInterval:_chordDuration @@ -295,10 +305,8 @@ - (void)updateChord:(int)keycode - (void)clearChord { _chordKeyCount = 0; - if (_chordTimer) { - if (_chordTimer.valid) { - [_chordTimer invalidate]; - } + if (_chordTimer.valid) { + [_chordTimer invalidate]; _chordTimer = nil; } } @@ -308,6 +316,17 @@ - (NSUInteger)recognizedEvents:(id)sender { return NSEventMaskKeyDown | NSEventMaskFlagsChanged; } +- (void)showInitialStatus { + RIME_STRUCT(RimeStatus, status); + if (_session && rime_get_api()->get_status(_session, &status)) { + _schemaId = @(status.schema_id); + NSString *schemaName = status.schema_name ? @(status.schema_name) : @(status.schema_id); + rime_get_api()->free_status(&status); + [NSApp.squirrelAppDelegate.panel updateStatusLong:schemaName statusShort:@""]; + [self rimeUpdate]; + } +} + - (void)activateServer:(id)sender { //NSLog(@"activateServer:"); NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config @@ -332,7 +351,6 @@ - (void)activateServer:(id)sender { @"ascii_composer/good_old_caps_lock"]; } [defaultConfig close]; - [super activateServer:sender]; } @@ -347,6 +365,9 @@ - (instancetype)initWithServer:(IMKServer *)server _originalString = [[NSMutableString alloc] init]; _composedString = [[NSMutableString alloc] init]; } + if (NSApp.squirrelAppDelegate.showNotifications == kShowNotificationsAlways) { + [self showInitialStatus]; + } return self; } @@ -432,6 +453,7 @@ - (NSArray *)candidates:(id)sender { - (void)hidePalettes { [NSApp.squirrelAppDelegate.panel hide]; + [super hidePalettes]; } - (void)dealloc { @@ -453,10 +475,9 @@ - (void)commitString:(id)string { //NSLog(@"commitString:"); [self.client insertText:string replacementRange:self.replacementRange]; - - [_preeditString deleteCharactersInRange:NSMakeRange(0, _preeditString.length)]; - [_originalString deleteCharactersInRange:NSMakeRange(0, _originalString.length)]; - [_composedString deleteCharactersInRange:NSMakeRange(0, _composedString.length)]; + _preeditString = nil; + _originalString = nil; + _composedString = nil; } - (void)cancelComposition { @@ -470,32 +491,40 @@ - (void)updateComposition { replacementRange:self.replacementRange]; } +- (void)showPlaceholder:(NSString *)placeholder { + NSDictionary* attrs = [self markForStyle:kTSMHiliteSelectedRawText + atRange:NSMakeRange(0, placeholder ? placeholder.length : 1)]; + _preeditString = [[NSMutableAttributedString alloc] initWithString:placeholder ? : @"█" + attributes:attrs]; + _caretPos = 0; + [self updateComposition]; +} + - (void)showPreeditString:(NSString *)preedit selRange:(NSRange)range caretPos:(NSUInteger)pos { //NSLog(@"showPreeditString: '%@'", preedit); - if (_inlinePlaceHolder && _candidates.count > 0 && preedit.length == 0) { - preedit = @" "; + if ([preedit isEqualToString:_preeditString.string] && + NSEqualRanges(range, _selRange) && pos == _caretPos) { + return; } _selRange = range; _caretPos = pos; //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", // range.location, range.length, pos); - NSDictionary *attrs; - _preeditString = [[NSMutableAttributedString alloc] initWithString:preedit]; + NSDictionary* attrs = [self markForStyle:kTSMHiliteSelectedRawText + atRange:NSMakeRange(0, preedit.length)]; + _preeditString = [[NSMutableAttributedString alloc] initWithString:preedit + attributes:attrs]; if (range.location > 0) { - NSRange convertedRange = NSMakeRange(0, range.location); - attrs = [self markForStyle:kTSMHiliteConvertedText atRange:convertedRange]; - [_preeditString addAttributes:attrs range:convertedRange]; + [_preeditString addAttributes:[self markForStyle:kTSMHiliteConvertedText + atRange:NSMakeRange(0, range.location)] + range:NSMakeRange(0, range.location)]; } if (range.location < pos) { - attrs = [self markForStyle:kTSMHiliteSelectedConvertedText atRange:range]; - [_preeditString addAttributes:attrs range:range]; - } - if (NSMaxRange(range) < preedit.length) { - NSRange rawRange = NSMakeRange(NSMaxRange(range), preedit.length - NSMaxRange(range)); - attrs = [self markForStyle:kTSMHiliteSelectedRawText atRange:rawRange]; - [_preeditString addAttributes:attrs range:rawRange]; + [_preeditString addAttributes:[self markForStyle:kTSMHiliteSelectedConvertedText + atRange:range] + range:range]; } [self updateComposition]; } @@ -505,67 +534,70 @@ - (void)showPanelWithPreedit:(NSString *)preedit caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - highlighted:(NSUInteger)index + highlightedIndex:(NSUInteger)highlightedIndex pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; - NSRect inputPos; - [self.client attributesForCharacterIndex:0 - lineHeightRectangle:&inputPos]; - if (NSEqualRects(inputPos, NSZeroRect) && _preeditString.length == 0) { - // activate inline session, in e.g. table cells, by fake inputs - [self.client setMarkedText:@" " - selectionRange:NSMakeRange(0, 0) - replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; - [self.client attributesForCharacterIndex:0 - lineHeightRectangle:&inputPos]; - [self.client setMarkedText:_preeditString - selectionRange:NSMakeRange(0, 0) - replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + NSRect IbeamRect; + [self.client attributesForCharacterIndex:0 + lineHeightRectangle:&IbeamRect]; + if (NSEqualRects(IbeamRect, NSZeroRect) && _preeditString.length == 0) { + if (self.client.selectedRange.length == 0) { + // activate inline session, in e.g. table cells, by fake inputs + [self.client setMarkedText:@" " + selectionRange:NSMakeRange(0, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + [self.client attributesForCharacterIndex:0 + lineHeightRectangle:&IbeamRect]; + [self.client setMarkedText:_preeditString + selectionRange:NSMakeRange(0, 0) + replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; + } else { + [self.client attributesForCharacterIndex:self.client.selectedRange.location + lineHeightRectangle:&IbeamRect]; + } } + NSWidth(IbeamRect) > NSHeight(IbeamRect) ? IbeamRect.origin.x += _inlineOffset + : IbeamRect.origin.y += _inlineOffset; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view - if (_goodOldCapsLock && _lastModifier & NSEventModifierFlagCapsLock) { + if (_goodOldCapsLock && (_lastModifiers & NSEventModifierFlagCapsLock)) { NSRect screenRect = NSScreen.mainScreen.frame; - if (NSIntersectsRect(inputPos, screenRect)) { + if (NSIntersectsRect(IbeamRect, screenRect)) { screenRect = NSScreen.mainScreen.visibleFrame; - if (NSWidth(inputPos) > NSHeight(inputPos)) { - NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos) - 30, NSMinY(inputPos), 27, NSHeight(inputPos)); + if (NSWidth(IbeamRect) > NSHeight(IbeamRect)) { + NSRect capslockAccessory = NSMakeRect(NSMinX(IbeamRect) - 30, NSMinY(IbeamRect), + 27, NSHeight(IbeamRect)); if (NSMinX(capslockAccessory) < NSMinX(screenRect)) capslockAccessory.origin.x = NSMinX(screenRect); if (NSMaxX(capslockAccessory) > NSMaxX(screenRect)) capslockAccessory.origin.x = NSMaxX(screenRect) - NSWidth(capslockAccessory); - inputPos = NSUnionRect(inputPos, capslockAccessory); + IbeamRect = NSUnionRect(IbeamRect, capslockAccessory); } else { - NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos), NSMinY(inputPos) - 26, NSWidth(inputPos), 23); + NSRect capslockAccessory = NSMakeRect(NSMinX(IbeamRect), NSMinY(IbeamRect) - 26, + NSWidth(IbeamRect), 23); if (NSMinY(capslockAccessory) < NSMinY(screenRect)) capslockAccessory.origin.y = NSMaxY(screenRect) + 3; if (NSMaxY(capslockAccessory) > NSMaxY(screenRect)) capslockAccessory.origin.y = NSMaxY(screenRect) - NSHeight(capslockAccessory); - inputPos = NSUnionRect(inputPos, capslockAccessory); + IbeamRect = NSUnionRect(IbeamRect, capslockAccessory); } } } } panel.inputController = self; - panel.position = inputPos; + panel.IbeamRect = IbeamRect; [panel showPreedit:preedit selRange:selRange caretPos:caretPos candidates:candidates comments:comments - highlighted:index + highlightedIndex:highlightedIndex pageNum:pageNum lastPage:lastPage]; } -@end // SquirrelController - - -// implementation of private interface -@implementation SquirrelInputController (Private) - - (void)createSession { NSString *app = self.client.bundleIdentifier; //NSLog(@"createSession: %@", app); @@ -579,15 +611,19 @@ - (void)createSession { SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:app]; if (appOptions) { for (NSString *key in appOptions) { - Bool value = appOptions[key].intValue; - //NSLog(@"set app option: %@ = %d", key, value); - rime_get_api()->set_option(_session, key.UTF8String, value); + NSNumber *number = appOptions[key]; + if (!strcmp(number.objCType, @encode(BOOL))) { + Bool value = number.intValue; + //NSLog(@"set app option: %@ = %d", key, value); + rime_get_api()->set_option(_session, key.UTF8String, value); + } } _panellessCommitFix = appOptions[@"panelless_commit_fix"].boolValue; - _inlinePlaceHolder = appOptions[@"inline_placeholder"].boolValue; + _inlinePlaceholder = appOptions[@"inline_placeholder"].boolValue; + _inlineOffset = appOptions[@"inline_offset"].intValue; } } - _lastModifier = 0; + _lastModifiers = 0; _lastEventCount = 0; [self rimeUpdate]; } @@ -602,70 +638,37 @@ - (void)destroySession { [self clearChord]; } -- (void)rimeConsumeCommittedText { +- (BOOL)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (rime_get_api()->get_commit(_session, &commit)) { NSString *commitText = @(commit.text); - if (_preeditString.length == 0 && _panellessCommitFix) { - [self showPreeditString:@" " - selRange:NSMakeRange(0, 0) - caretPos:0]; + if (_panellessCommitFix) { + [self showPlaceholder:commitText]; + [self commitString:commitText]; + [self showPlaceholder:sizeof(commit.text) == 1 ? @"" : nil]; + } else { + [self commitString:commitText]; } - [self commitString:commitText]; rime_get_api()->free_commit(&commit); + return YES; } -} - -- (void)updateStyleOptions { - // update the list of switchers that change styles and color-themes - SquirrelOptionSwitcher *optionSwitcher; - SquirrelConfig *schema = [[SquirrelConfig alloc] init]; - if ([schema openWithSchemaId:_schemaId - baseConfig:NSApp.squirrelAppDelegate.config] && - [schema hasSection:@"style"]) { - optionSwitcher = [schema getOptionSwitcher]; - } else { - optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:_schemaId - switcher:@{} - optionGroups:@{}]; - } - [schema close]; - NSMutableDictionary *switcher = optionSwitcher.mutableSwitcher; - NSSet *prevStates = [NSSet setWithArray:optionSwitcher.optionStates]; - for (NSString *state in prevStates) { - NSString *updatedState; - NSArray *optionGroup = [optionSwitcher.switcher allKeysForObject:state]; - for (NSString *option in optionGroup) { - if (rime_get_api()->get_option(_session, option.UTF8String)) { - updatedState = option; - break; - } - } - updatedState = updatedState ? : [@"!" stringByAppendingString:optionGroup[0]]; - if (![updatedState isEqualToString:state]) { - for (NSString *option in optionGroup) { - switcher[option] = updatedState; - } - } - } - [optionSwitcher updateSwitcher:switcher]; - NSApp.squirrelAppDelegate.panel.optionSwitcher = optionSwitcher; + return NO; } - (void)rimeUpdate { //NSLog(@"rimeUpdate"); - [self rimeConsumeCommittedText]; + BOOL didCommit = [self rimeConsumeCommittedText]; RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style if (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id)) { _schemaId = @(status.schema_id); - _showingSwitcherMenu = rime_get_api()->get_option(_session, "dumb"); + _showingSwitcherMenu = (BOOL)rime_get_api()->get_option(_session, "dumb"); if (!_showingSwitcherMenu) { - [self updateStyleOptions]; [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; - [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; + [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId + withRimeSession:_session]; // inline preedit _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || @@ -683,9 +686,10 @@ - (void)rimeUpdate { RIME_STRUCT(RimeContext, ctx); if (rime_get_api()->get_context(_session, &ctx)) { + BOOL showingStatus = NSApp.squirrelAppDelegate.panel.statusMessage.length > 0; // update raw input const char *raw_input = rime_get_api()->get_input(_session); - [_originalString setString:raw_input ? @(raw_input) : @""]; + _originalString = raw_input ? @(raw_input) : @""; // update preedit text const char *preedit = ctx.composition.preedit; @@ -693,20 +697,19 @@ - (void)rimeUpdate { // update composed string if (!preedit || _showingSwitcherMenu) { - [_composedString deleteCharactersInRange:NSMakeRange(0, _composedString.length)]; + _composedString = @""; } else if (rime_get_api()->get_option(_session, "soft_cursor")) { - size_t cursorPos = (size_t)ctx.composition.cursor_pos - (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0); + size_t cursorPos = (size_t)ctx.composition.cursor_pos - + (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0); char composed[strlen(preedit) - 2]; - for (size_t i = 0; i < strlen(preedit) - 3; ++i) { - composed[i] = preedit[i < cursorPos ? i : i + 3]; - } - composed[strlen(preedit) - 3] = '\0'; - [_composedString setString:[@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]]; + strlcpy(composed, preedit, cursorPos + 1); + strlcat(composed, preedit + cursorPos + 3, strlen(preedit) - 2); + _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; } else { - [_composedString setString:[@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]]; + _composedString = [@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]; } - NSUInteger start = [[NSString alloc] initWithBytes:preedit + NSUInteger start = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; NSUInteger end = [[NSString alloc] initWithBytes:preedit @@ -720,24 +723,12 @@ - (void)rimeUpdate { encoding:NSUTF8StringEncoding].length; NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; - // update candidates - NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate]; - NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate]; - for (NSUInteger i = 0; i < numCandidate; ++i) { - [candidates addObject:@(ctx.menu.candidates[i].text)]; - [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; - } - [self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText - selRange:NSMakeRange(start, end - start) - caretPos:_showingSwitcherMenu ? NSNotFound : caretPos - candidates:candidates - comments:comments - highlighted:(NSUInteger)ctx.menu.highlighted_candidate_index - pageNum:(NSUInteger)ctx.menu.page_no - lastPage:(BOOL)ctx.menu.is_last_page]; - - if (!_showingSwitcherMenu) { - if (_inlineCandidate) { + if (!showingStatus) { + if (_showingSwitcherMenu) { + if (_inlinePlaceholder) { + [self updateComposition]; + } + } else if (_inlineCandidate) { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit) { @@ -745,9 +736,11 @@ - (void)rimeUpdate { candidatePreviewText = [candidatePreviewText stringByAppendingString: [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; } - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) - caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (length - caretPos)]; + if (!didCommit || candidatePreviewText.length > 0) { + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) + caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (length - caretPos)]; + } } else { if ((end < caretPos) && (caretPos > start)) { candidatePreviewText = [candidatePreviewText substringWithRange: @@ -756,25 +749,52 @@ - (void)rimeUpdate { candidatePreviewText = [candidatePreviewText substringWithRange: NSMakeRange(0, candidatePreviewText.length - (length - end))]; } - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) - caretPos:(caretPos < end ? caretPos : candidatePreviewText.length)]; + if (!didCommit || candidatePreviewText.length > 0) { + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start - (caretPos < end), + candidatePreviewText.length - start + (caretPos < end)) + caretPos:caretPos < end ? caretPos : candidatePreviewText.length]; + } } } else { if (_inlinePreedit && !_showingSwitcherMenu) { - [self showPreeditString:preeditText - selRange:NSMakeRange(start, end - start) - caretPos:caretPos]; + if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) { + [self showPlaceholder:kFullWidthSpace]; + } else if (!didCommit || preeditText.length > 0) { + [self showPreeditString:preeditText + selRange:NSMakeRange(start, end - start) + caretPos:caretPos]; + } } else { - [self showPreeditString:@"" - selRange:NSMakeRange(0, 0) - caretPos:0]; + if (_inlinePlaceholder && preedit) { + [self showPlaceholder:kFullWidthSpace]; + } else if (!didCommit || preedit) { + [self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0]; + } } } } + // update candidates + NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + for (NSUInteger i = 0; i < numCandidate; ++i) { + [candidates addObject:@(ctx.menu.candidates[i].text)]; + [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; + } + [self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText + selRange:NSMakeRange(start, end - start) + caretPos:_showingSwitcherMenu ? NSNotFound : caretPos + candidates:candidates + comments:comments + highlightedIndex:(NSUInteger)ctx.menu.highlighted_candidate_index + pageNum:(NSUInteger)ctx.menu.page_no + lastPage:(BOOL)ctx.menu.is_last_page]; rime_get_api()->free_context(&ctx); } else { [self hidePalettes]; + _preeditString = nil; + _composedString = nil; + _originalString = nil; } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 1533fbf2c..5117fd190 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -12,20 +12,24 @@ typedef enum { @interface SquirrelPanel : NSPanel -// Linear candidate list, as opposed to stacked candidate list. +// Linear candidate list layout, as opposed to stacked candidate list layout. @property(nonatomic, readonly) BOOL linear; -// Tabled candidate list, a subtype of linear candidate list with tabled layout. +// Tabled candidate list layout, a subtype of linear candidate list with tabled layout. @property(nonatomic, readonly) BOOL tabled; -// Vertical text, as opposed to horizontal text. +// Vertical text orientation, as opposed to horizontal text orientation. @property(nonatomic, readonly) BOOL vertical; // Show preedit text inline. @property(nonatomic, readonly) BOOL inlinePreedit; -// Show first candidate inline +// Show primary candidate inline @property(nonatomic, readonly) BOOL inlineCandidate; // Store switch options that change style (color theme) settings @property(nonatomic, strong) SquirrelOptionSwitcher *optionSwitcher; -// position of input caret on screen. -@property(nonatomic, assign) NSRect position; +// option(s) on Chinese script (simplification/traditional, or radio group thereof) +@property(nonatomic, strong) NSArray *scriptOptions; +// position of the text input I-beam cursor on screen. +@property(nonatomic, assign) NSRect IbeamRect; + +@property(nonatomic, strong) NSString *statusMessage; @property(nonatomic, assign) SquirrelInputController *inputController; @@ -34,7 +38,7 @@ typedef enum { caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - highlighted:(NSUInteger)highlighted + highlightedIndex:(NSUInteger)highlightedIndex pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index b8055d0e1..349fccbbc 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -7,9 +7,9 @@ static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; static const NSTimeInterval kShowStatusDuration = 1.2; -static NSString *kDefaultCandidateFormat = @"%c. %@"; -static NSString *kTipSpecifier = @"%s"; -static NSString *kFullWidthSpace = @" "; +static NSString *const kDefaultCandidateFormat = @"%c. %@"; +static NSString *const kTipSpecifier = @"%s"; +static NSString *const kFullWidthSpace = @" "; @implementation NSBezierPath (BezierPathQuartzUtilities) @@ -55,7 +55,7 @@ - (CGPathRef)quartzPath { if (!didClosePath) { CGPathCloseSubpath(path); } - immutablePath = CFAutorelease(CGPathCreateCopy(path)); + immutablePath = (CGPathRef)CFAutorelease(CGPathCreateCopy(path)); CGPathRelease(path); } return immutablePath; @@ -67,38 +67,46 @@ - (CGPathRef)quartzPath { @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) +static NSString *const kMarkDownPattern = +@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)"; +static NSString *const kRubyPattern = @"(\uFFF9\\s*)(\\S+?)(\\s*\uFFFA(.+?)\uFFFB)"; + - (void)superscriptRange:(NSRange)range { - [self enumerateAttribute:NSFontAttributeName inRange:range + [self enumerateAttribute:NSFontAttributeName + inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + usingBlock:^(NSFont *value, NSRange subRange, BOOL *stop) { NSFont *font = [NSFont fontWithDescriptor:value.fontDescriptor size:floor(value.pointSize * 0.55)]; - [self addAttributes:@{ NSFontAttributeName : font, - (NSString *)kCTBaselineClassAttributeName : (NSString *)kCTBaselineClassIdeographicHigh, - NSSuperscriptAttributeName : @(1)} - range:range]; + [self addAttributes:@{ NSFontAttributeName:font, + (NSString *)kCTBaselineClassAttributeName:(NSString *)kCTBaselineClassIdeographicHigh, + NSSuperscriptAttributeName:@(1)} + range:subRange]; }]; } - (void)subscriptRange:(NSRange)range { - [self enumerateAttribute:NSFontAttributeName inRange:range + [self enumerateAttribute:NSFontAttributeName + inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(NSFont *value, NSRange range, BOOL *stop) { + usingBlock:^(NSFont *value, NSRange subRange, BOOL *stop) { NSFont *font = [NSFont fontWithDescriptor:value.fontDescriptor size:floor(value.pointSize * 0.55)]; - [self addAttributes:@{ NSFontAttributeName : font, - (NSString *)kCTBaselineClassAttributeName : (NSString *)kCTBaselineClassIdeographicLow, - NSSuperscriptAttributeName : @(-1)} - range:range]; + [self addAttributes:@{ NSFontAttributeName:font, + (NSString *)kCTBaselineClassAttributeName:(NSString *)kCTBaselineClassIdeographicLow, + NSSuperscriptAttributeName:@(-1)} + range:subRange]; }]; } - (void)formatMarkDown { - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: - @"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)" - options:NSRegularExpressionUseUnicodeWordBoundaries error:nil]; + NSRegularExpression *regex = [[NSRegularExpression alloc] + initWithPattern:kMarkDownPattern + options:NSRegularExpressionUseUnicodeWordBoundaries + error:nil]; NSInteger __block offset = 0; - [regex enumerateMatchesInString:self.string options:0 + [regex enumerateMatchesInString:self.string + options:0 range:NSMakeRange(0, self.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { result = [result resultByAdjustingRangesWithOffset:offset]; @@ -109,7 +117,7 @@ - (void)formatMarkDown { range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"*"] || [tag isEqualToString:@"_"] || [tag isEqualToString:@""] || [tag isEqualToString:@""]) { - [self applyFontTraits:NSItalicFontMask + [self applyFontTraits:NSItalicFontMask range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@""]) { [self addAttribute:NSUnderlineStyleAttributeName @@ -117,7 +125,7 @@ - (void)formatMarkDown { range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"~~"] || [tag isEqualToString:@""]) { [self addAttribute:NSStrikethroughStyleAttributeName - value:@(NSUnderlineStyleSingle) + value:@(NSUnderlineStyleSingle) range:[result rangeAtIndex:5]]; } else if ([tag isEqualToString:@"^"] || [tag isEqualToString:@""]) { [self superscriptRange:[result rangeAtIndex:5]]; @@ -136,11 +144,13 @@ - (void)formatMarkDown { - (CGFloat)annotateRubyInRange:(NSRange)range verticalOrientation:(BOOL)isVertical maximumLength:(CGFloat)maxLength { - NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern: - @"(\uFFF9\\s*)(\\S+?)(\\s*\uFFFA(.+?)\uFFFB)" options:0 error:nil]; + NSRegularExpression *regex = [[NSRegularExpression alloc] + initWithPattern:kRubyPattern options:0 error:nil]; CGFloat __block rubyLineHeight = 0.0; NSInteger __block offset = 0; - [regex enumerateMatchesInString:self.mutableString options:0 range:range + [regex enumerateMatchesInString:self.mutableString + options:0 + range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { result = [result resultByAdjustingRangesWithOffset:offset]; NSRange baseRange = [result rangeAtIndex:2]; @@ -155,38 +165,52 @@ - (CGFloat)annotateRubyInRange:(NSRange)range NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:baseRange.location effectiveRange:NULL]; - baseFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)baseFont, (CFStringRef)self.string, - CFRangeMake((CFIndex)baseRange.location, (CFIndex)baseRange.length), CFSTR("zh"))); + baseFont = CFBridgingRelease(CTFontCreateForStringWithLanguage + ((CTFontRef)baseFont, (CFStringRef)self.string, + CFRangeMake((CFIndex)baseRange.location, (CFIndex)baseRange.length), + CFSTR("zh"))); [self addAttribute:NSFontAttributeName value:baseFont range:baseRange]; CGFloat rubyScale = 0.5; - CFStringRef rubyString = (__bridge CFStringRef)[self.string substringWithRange:[result rangeAtIndex:4]]; + CFStringRef rubyString = (__bridge CFStringRef)[self.string substringWithRange: + [result rangeAtIndex:4]]; NSFont *rubyFont = [self attribute:NSFontAttributeName - atIndex:[result rangeAtIndex:4].location + atIndex:[result rangeAtIndex:4].location effectiveRange:NULL]; rubyFont = [NSFont fontWithDescriptor:rubyFont.fontDescriptor size:rubyFont.pointSize * rubyScale]; - rubyFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)rubyFont, rubyString, - CFRangeMake(0, CFStringGetLength(rubyString)), CFSTR("zh"))); - rubyLineHeight = MAX(rubyLineHeight, isVertical ? rubyFont.verticalFont.ascender - rubyFont.verticalFont.descender - : rubyFont.ascender - rubyFont.descender); + rubyFont = CFBridgingRelease(CTFontCreateForStringWithLanguage + ((CTFontRef)rubyFont, rubyString, + CFRangeMake(0, CFStringGetLength(rubyString)), + CFSTR("zh"))); + rubyFont = isVertical ? rubyFont.verticalFont : rubyFont; + rubyLineHeight = MAX(rubyLineHeight, rubyFont.ascender - rubyFont.descender); CGColorRef rubyColor = [[self attribute:NSForegroundColorAttributeName atIndex:[result rangeAtIndex:4].location effectiveRange:NULL] CGColor]; - CFTypeRef keys[] = {kCTFontAttributeName, kCTForegroundColorAttributeName, - kCTBaselineClassAttributeName, kCTRubyAnnotationSizeFactorAttributeName, + CFTypeRef keys[] = {kCTFontAttributeName, + kCTForegroundColorAttributeName, + kCTBaselineClassAttributeName, + kCTRubyAnnotationSizeFactorAttributeName, kCTRubyAnnotationScaleToFitAttributeName}; - CFTypeRef values[] = {(__bridge CTFontRef)rubyFont, rubyColor, kCTBaselineClassIdeographicHigh, - CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), kCFBooleanFalse}; - CFDictionaryRef rubyAttrs = CFDictionaryCreate(NULL, keys, values, 5, &kCFTypeDictionaryKeyCallBacks, + CFTypeRef values[] = {(__bridge CTFontRef)rubyFont, + rubyColor, + kCTBaselineClassIdeographicHigh, + CFNumberCreate(NULL, kCFNumberDoubleType, &rubyScale), + kCFBooleanFalse}; + CFDictionaryRef rubyAttrs = CFDictionaryCreate(NULL, keys, values, 5, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CTRubyAnnotationRef rubyAnnotation = CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, - kCTRubyOverhangNone, kCTRubyPositionBefore, rubyString, rubyAttrs); + CTRubyAnnotationRef rubyAnnotation = + CTRubyAnnotationCreateWithAttributes(kCTRubyAlignmentDistributeSpace, + kCTRubyOverhangNone, + kCTRubyPositionBefore, + rubyString, rubyAttrs); [self deleteCharactersInRange:[result rangeAtIndex:3]]; if (@available(macOS 12.0, *)) { - [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName : CFBridgingRelease(rubyAnnotation), - NSVerticalGlyphFormAttributeName : @(isVertical)} + [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName:CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName:@(isVertical)} range:baseRange]; [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:3].length + [result rangeAtIndex:1].length; @@ -195,8 +219,8 @@ - (CGFloat)annotateRubyInRange:(NSRange)range [self replaceCharactersInRange:NSMakeRange(NSMaxRange(baseRange), 0) withString:[NSString stringWithFormat:@"%C", 0x8B]]; baseRange.length += 1; - [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName : CFBridgingRelease(rubyAnnotation), - NSVerticalGlyphFormAttributeName : @(isVertical)} + [self addAttributes:@{(NSString *)kCTRubyAnnotationAttributeName:CFBridgingRelease(rubyAnnotation), + NSVerticalGlyphFormAttributeName:@(isVertical)} range:baseRange]; [self deleteCharactersInRange:[result rangeAtIndex:1]]; offset -= [result rangeAtIndex:3].length - 1 + [result rangeAtIndex:1].length; @@ -222,8 +246,8 @@ + (NSColorSpace *)labColorSpace { CGFloat blackPoint[3] = {0.0, 0.0, 0.0}; CGFloat range[4] = {-127.0, 127.0, -127.0, 127.0}; CGColorSpaceRef colorSpaceLab = CGColorSpaceCreateLab(whitePoint, blackPoint, range); - NSColorSpace *labColorSpace = [[NSColorSpace alloc] initWithCGColorSpace:colorSpaceLab]; - CGColorSpaceRelease(colorSpaceLab); + NSColorSpace *labColorSpace = [[NSColorSpace alloc] initWithCGColorSpace: + (CGColorSpaceRef)CFAutorelease(colorSpaceLab)]; return labColorSpace; } @@ -291,17 +315,23 @@ - (NSColor *)invertLuminanceWithAdjustment:(NSInteger)sign { @interface SquirrelTheme : NSObject -@property(nonatomic, strong, readonly) NSColor *backgroundColor; -@property(nonatomic, strong, readonly) NSImage *backgroundImage; -@property(nonatomic, strong, readonly) NSColor *highlightedStripColor; -@property(nonatomic, strong, readonly) NSColor *highlightedPreeditColor; -@property(nonatomic, strong, readonly) NSColor *preeditBackgroundColor; +typedef enum { + kStatusMessageTypeMixed = 0, + kStatusMessageTypeShort = 1, + kStatusMessageTypeLong = 2 +} SquirrelStatusMessageType; + +@property(nonatomic, strong, readonly) NSColor *backColor; +@property(nonatomic, strong, readonly) NSColor *highlightedCandidateBackColor; +@property(nonatomic, strong, readonly) NSColor *highlightedPreeditBackColor; +@property(nonatomic, strong, readonly) NSColor *preeditBackColor; @property(nonatomic, strong, readonly) NSColor *borderColor; +@property(nonatomic, strong, readonly) NSImage *backImage; @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat highlightedCornerRadius; @property(nonatomic, readonly) CGFloat separatorWidth; -@property(nonatomic, readonly) NSSize edgeInset; +@property(nonatomic, readonly) NSSize borderInset; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @@ -314,6 +344,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @property(nonatomic, readonly) BOOL inlineCandidate; +@property(nonatomic, readonly) SquirrelStatusMessageType statusMessageType; @property(nonatomic, strong, readonly) NSDictionary *attrs; @property(nonatomic, strong, readonly) NSDictionary *highlightedAttrs; @@ -344,19 +375,18 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSArray *labels; @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; -@property(nonatomic, strong, readonly) NSString *statusMessageType; -- (void)setBackgroundColor:(NSColor *)backgroundColor - backgroundImage:(NSImage *)backgroundImage - highlightedStripColor:(NSColor *)highlightedStripColor - highlightedPreeditColor:(NSColor *)highlightedPreeditColor - preeditBackgroundColor:(NSColor *)preeditBackgroundColor - borderColor:(NSColor *)borderColor; +- (void) setBackColor:(NSColor *)backColor + highlightedCandidateBackColor:(NSColor *)highlightedCandidateBackColor + highlightedPreeditBackColor:(NSColor *)highlightedPreeditBackColor + preeditBackColor:(NSColor *)preeditBackColor + borderColor:(NSColor *)borderColor + backImage:(NSImage *)backImage; - (void) setCornerRadius:(CGFloat)cornerRadius highlightedCornerRadius:(CGFloat)highlightedCornerRadius separatorWidth:(CGFloat)separatorWidth - edgeInset:(NSSize)edgeInset + borderInset:(NSSize)borderInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha @@ -395,7 +425,7 @@ - (void)setCandidateFormat:(NSString *)candidateFormat; - (void)updateCandidateFormats; -- (void)setStatusMessageType:(NSString *)statusMessageType; +- (void)setStatusMessageType:(NSString *)type; - (void)setAnnotationHeight:(CGFloat)height; @@ -406,8 +436,7 @@ @implementation SquirrelTheme static NSArray * formatLabels(NSAttributedString *format, NSArray *labels) { NSRange enumRange = NSMakeRange(0, 0); - NSMutableArray *formatted = - [[NSMutableArray alloc] initWithCapacity:labels.count]; + NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString: [labels componentsJoinedByString:@""]]; if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xFF10, 10)] @@ -427,7 +456,7 @@ @implementation SquirrelTheme const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0x24EA : [label characterAtIndex:0] - 0xFF11 + 0x2460, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; - [newFormat replaceCharactersInRange:enumRange + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; [formatted addObject:newFormat]; } @@ -469,7 +498,7 @@ @implementation SquirrelTheme for (NSString *label in labels) { const unichar chars[] = {[label characterAtIndex:0] - 0xFF21 + 0x24B6, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; - [newFormat replaceCharactersInRange:enumRange + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; [formatted addObject:newFormat]; } @@ -487,7 +516,7 @@ @implementation SquirrelTheme for (NSString *label in labels) { const unichar chars[] = {0xD83C, [label characterAtIndex:0] - 0xFF21 + 0xDD30, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; - [newFormat replaceCharactersInRange:enumRange + [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; [formatted addObject:newFormat]; } @@ -504,24 +533,24 @@ @implementation SquirrelTheme return formatted; } -- (void)setBackgroundColor:(NSColor *)backgroundColor - backgroundImage:(NSImage *)backgroundImage - highlightedStripColor:(NSColor *)highlightedStripColor - highlightedPreeditColor:(NSColor *)highlightedPreeditColor - preeditBackgroundColor:(NSColor *)preeditBackgroundColor - borderColor:(NSColor *)borderColor { - _backgroundColor = backgroundColor; - _backgroundImage = backgroundImage; - _highlightedStripColor = highlightedStripColor; - _highlightedPreeditColor = highlightedPreeditColor; - _preeditBackgroundColor = preeditBackgroundColor; +- (void) setBackColor:(NSColor *)backColor + highlightedCandidateBackColor:(NSColor *)highlightedCandidateBackColor + highlightedPreeditBackColor:(NSColor *)highlightedPreeditBackColor + preeditBackColor:(NSColor *)preeditBackColor + borderColor:(NSColor *)borderColor + backImage:(NSImage *)backImage { + _backColor = backColor; + _highlightedCandidateBackColor = highlightedCandidateBackColor; + _highlightedPreeditBackColor = highlightedPreeditBackColor; + _preeditBackColor = preeditBackColor; _borderColor = borderColor; + _backImage = backImage; } - (void) setCornerRadius:(CGFloat)cornerRadius highlightedCornerRadius:(CGFloat)highlightedCornerRadius separatorWidth:(CGFloat)separatorWidth - edgeInset:(NSSize)edgeInset + borderInset:(NSSize)borderInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha @@ -537,7 +566,7 @@ - (void) setCornerRadius:(CGFloat)cornerRadius _cornerRadius = cornerRadius; _highlightedCornerRadius = highlightedCornerRadius; _separatorWidth = separatorWidth; - _edgeInset = edgeInset; + _borderInset = borderInset; _linespace = linespace; _preeditLinespace = preeditLinespace; _alpha = alpha; @@ -578,9 +607,9 @@ - (void) setAttrs:(NSDictionary *)attrs NSMutableDictionary *sepAttrs = commentAttrs.mutableCopy; sepAttrs[NSVerticalGlyphFormAttributeName] = @(NO); sepAttrs[NSKernAttributeName] = @(0.0); - _separator = [[NSAttributedString alloc] initWithString:_linear ? - (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] : kFullWidthSpace) : @"\n" - attributes:sepAttrs]; + _separator = [[NSAttributedString alloc] initWithString: + _linear ? (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] + : kFullWidthSpace) : @"\n" attributes:sepAttrs]; // Symbols for function buttons NSTextAttachment *attmLeftFill = [[NSTextAttachment alloc] init]; @@ -589,7 +618,7 @@ - (void) setAttrs:(NSDictionary *)attrs attmUpFill.image = [NSImage imageNamed:@"Symbols/chevron.up.circle.fill"]; NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; attrsBackFill[NSAttachmentAttributeName] = _linear ? attmUpFill : attmLeftFill; - _symbolBackFill = [[NSAttributedString alloc] + _symbolBackFill = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackFill]; NSTextAttachment *attmLeftStroke = [[NSTextAttachment alloc] init]; @@ -598,7 +627,7 @@ - (void) setAttrs:(NSDictionary *)attrs attmUpStroke.image = [NSImage imageNamed:@"Symbols/chevron.up.circle"]; NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; attrsBackStroke[NSAttachmentAttributeName] = _linear ? attmUpStroke : attmLeftStroke; - _symbolBackStroke = [[NSAttributedString alloc] + _symbolBackStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsBackStroke]; NSTextAttachment *attmRightFill = [[NSTextAttachment alloc] init]; @@ -616,7 +645,7 @@ - (void) setAttrs:(NSDictionary *)attrs attmDownStroke.image = [NSImage imageNamed:@"Symbols/chevron.down.circle"]; NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; attrsForwardStroke[NSAttachmentAttributeName] = _linear ? attmDownStroke : attmRightStroke; - _symbolForwardStroke = [[NSAttributedString alloc] + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:@"\uFFFC" attributes:attrsForwardStroke]; NSTextAttachment *attmDeleteFill = [[NSTextAttachment alloc] init]; @@ -648,7 +677,7 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle _statusParagraphStyle = statusParagraphStyle; } -- (void)setSelectKeys:(NSString *)selectKeys +- (void)setSelectKeys:(NSString *)selectKeys labels:(NSArray *)labels directUpdate:(BOOL)update { _selectKeys = selectKeys; @@ -682,7 +711,8 @@ - (void)updateCandidateFormats { NSRange commentRange = NSMakeRange(NSMaxRange(candidateRange), candidateFormat.length - NSMaxRange(candidateRange)); // parse markdown formats - NSMutableAttributedString *format = [[NSMutableAttributedString alloc] initWithString:candidateFormat]; + NSMutableAttributedString *format = [[NSMutableAttributedString alloc] + initWithString:candidateFormat]; NSMutableAttributedString *highlightedFormat = format.mutableCopy; [format addAttributes:_labelAttrs range:labelRange]; [highlightedFormat addAttributes:_labelHighlightedAttrs range:labelRange]; @@ -700,24 +730,30 @@ - (void)updateCandidateFormats { format.length - NSMaxRange(candidateRange)); if (commentRange.length > 0) { [format replaceCharactersInRange:commentRange withString: - [kTipSpecifier stringByAppendingString:[format.string substringWithRange:commentRange]]]; + [kTipSpecifier stringByAppendingString: + [format.string substringWithRange:commentRange]]]; [highlightedFormat replaceCharactersInRange:commentRange withString: - [kTipSpecifier stringByAppendingString:[highlightedFormat.string substringWithRange:commentRange]]]; + [kTipSpecifier stringByAppendingString: + [highlightedFormat.string substringWithRange:commentRange]]]; } else { - [format appendAttributedString:[[NSAttributedString alloc] initWithString: - kTipSpecifier attributes:_commentAttrs]]; - [highlightedFormat appendAttributedString:[[NSAttributedString alloc] initWithString: - kTipSpecifier attributes:_commentHighlightedAttrs]]; + [format appendAttributedString: + [[NSAttributedString alloc] initWithString:kTipSpecifier + attributes:_commentAttrs]]; + [highlightedFormat appendAttributedString: + [[NSAttributedString alloc] initWithString:kTipSpecifier + attributes:_commentHighlightedAttrs]]; } _candidateFormats = formatLabels(format, _labels); _candidateHighlightedFormats = formatLabels(highlightedFormat, _labels); } - (void)setStatusMessageType:(NSString *)type { - if ([type isEqualToString:@"long"] || [type isEqualToString:@"short"] || [type isEqualToString:@"mix"]) { - _statusMessageType = type; + if ([type isEqualToString:@"long"]) { + _statusMessageType = kStatusMessageTypeLong; + } else if ([type isEqualToString:@"short"]) { + _statusMessageType = kStatusMessageTypeShort; } else { - _statusMessageType = @"mix"; + _statusMessageType = kStatusMessageTypeMixed; } } @@ -733,7 +769,7 @@ - (void)setAnnotationHeight:(CGFloat)height { @end // SquirrelTheme -#pragma mark - Typesetting extensions for TextKit 1 (macOS 11 or lower) +#pragma mark - Typesetting extensions for TextKit 1 (Mac OSX 10.9 to MacOS 11) @interface SquirrelLayoutManager : NSLayoutManager @end @@ -742,20 +778,22 @@ @implementation SquirrelLayoutManager - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange atPoint:(NSPoint)origin { - NSRange charRange = [self characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; + NSRange charRange = [self characterRangeForGlyphRange:glyphRange + actualGlyphRange:NULL]; NSTextContainer *textContainer = [self textContainerForGlyphAtIndex:glyphRange.location - effectiveRange:NULL withoutAdditionalLayout:YES]; - BOOL verticalOrientation = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + effectiveRange:NULL + withoutAdditionalLayout:YES]; + BOOL verticalOrientation = (BOOL)textContainer.layoutOrientation; CGContextRef context = NSGraphicsContext.currentContext.CGContext; CGContextResetClip(context); - [self.textStorage + [self.textStorage enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired - usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { NSRange glyRange = [self glyphRangeForCharacterRange:range actualCharacterRange:NULL]; NSRect lineRect = [self lineFragmentRectForGlyphAtIndex:glyRange.location - effectiveRange:NULL + effectiveRange:NULL withoutAdditionalLayout:YES]; CGContextSaveGState(context); if (attrs[(NSString *)kCTRubyAnnotationAttributeName]) { @@ -763,47 +801,50 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange NSUInteger glyphIndex = glyRange.location; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef) [self.textStorage attributedSubstringFromRange:range]); - CFArrayRef runs = CTLineGetGlyphRuns(line); + CFArrayRef runs = CTLineGetGlyphRuns((CTLineRef)CFAutorelease(line)); for (CFIndex i = 0; i < CFArrayGetCount(runs); ++i) { CGPoint position = [self locationForGlyphAtIndex:glyphIndex]; - NSPoint backingTextPosition = [textContainer.textView convertPointToBacking: - NSMakePoint(lineRect.origin.x + position.x, - lineRect.origin.y + position.y)]; - NSPoint textPosition = [textContainer.textView convertPointFromBacking: - NSMakePoint(round(backingTextPosition.x), - round(backingTextPosition.y))]; - CTRunRef run = CFArrayGetValueAtIndex(runs, i); + CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(runs, i); CGAffineTransform matrix = CTRunGetTextMatrix(run); - matrix.tx = textPosition.x; - matrix.ty = -textPosition.y; + CGPoint glyphOrigin = [textContainer.textView convertPointToBacking: + CGPointMake(origin.x + lineRect.origin.x + position.x, + -origin.y - lineRect.origin.y - position.y)]; + glyphOrigin = [textContainer.textView convertPointFromBacking: + CGPointMake(round(glyphOrigin.x), round(glyphOrigin.y))]; + matrix.tx = glyphOrigin.x; + matrix.ty = glyphOrigin.y; CGContextSetTextMatrix(context, matrix); CTRunDraw(run, context, CFRangeMake(0, 0)); glyphIndex += (NSUInteger)CTRunGetGlyphCount(run); } - CFRelease(line); } else { NSPoint position = [self locationForGlyphAtIndex:glyRange.location]; - position.x += origin.x + lineRect.origin.x; - position.y += origin.y + lineRect.origin.y; - CGContextSetTextPosition(context, position.x, position.y); + position.x += lineRect.origin.x; + position.y += lineRect.origin.y; + NSPoint backingPosition = [textContainer.textView convertPointToBacking:position]; + position = [textContainer.textView convertPointFromBacking: + NSMakePoint(round(backingPosition.x), round(backingPosition.y))]; NSFont *runFont = attrs[NSFontAttributeName]; - NSPoint glyphOffset = NSZeroPoint; - if (verticalOrientation && runFont.pointSize < 24 && - [runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + NSString *baselineClass = attrs[(NSString *)kCTBaselineClassAttributeName]; + NSPoint offset = origin; + if (!verticalOrientation && + ([baselineClass isEqualToString:(NSString *)kCTBaselineClassIdeographicCentered] || + [baselineClass isEqualToString:(NSString *)kCTBaselineClassMath])) { + NSFont *refFont = attrs[(NSString *)kCTBaselineReferenceInfoAttributeName][(NSString *)kCTBaselineReferenceFont]; + offset.y += runFont.ascender * 0.5 + runFont.descender * 0.5 - refFont.ascender * 0.5 - refFont.descender * 0.5; + } else if (verticalOrientation && runFont.pointSize < 24 && + [runFont.fontName isEqualToString:@"AppleColorEmoji"]) { NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; - glyphOffset.x = runFont.capHeight - runFont.pointSize; - glyphOffset.y = (runFont.capHeight - runFont.pointSize) * - (superscript == 0 ? 0.5 : (superscript == 1 ? 1.0 / 0.55 - 0.55 : 0.0)); + offset.x += runFont.capHeight - runFont.pointSize; + offset.y += (runFont.capHeight - runFont.pointSize) * + (superscript == 0 ? 0.5 : (superscript == 1 ? 1.0 / 0.55 - 0.55 : 0.0)); } - NSPoint backingGlyphOrigin = [textContainer.textView convertPointToBacking: - NSMakePoint(position.x + glyphOffset.x, - position.y + glyphOffset.y)]; - NSPoint glyphOrigin = [textContainer.textView convertPointFromBacking: - NSMakePoint(round(backingGlyphOrigin.x), - round(backingGlyphOrigin.y))]; - [super drawGlyphsForGlyphRange:glyRange - atPoint:NSMakePoint(glyphOrigin.x - position.x, - glyphOrigin.y - position.y)]; + NSPoint glyphOrigin = [textContainer.textView convertPointToBacking: + NSMakePoint(position.x + offset.x, position.y + offset.y)]; + glyphOrigin = [textContainer.textView convertPointFromBacking: + NSMakePoint(round(glyphOrigin.x), round(glyphOrigin.y))]; + [super drawGlyphsForGlyphRange:glyRange atPoint: + NSMakePoint(glyphOrigin.x - position.x, glyphOrigin.y - position.y)]; } CGContextRestoreGState(context); }]; @@ -813,38 +854,34 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange - (BOOL) layoutManager:(NSLayoutManager *)layoutManager shouldSetLineFragmentRect:(inout NSRect *)lineFragmentRect lineFragmentUsedRect:(inout NSRect *)lineFragmentUsedRect - baselineOffset:(inout CGFloat *)baselineOffset + baselineOffset:(inout CGFloat *)baselineOffset inTextContainer:(NSTextContainer *)textContainer forGlyphRange:(NSRange)glyphRange { - BOOL verticalOrientation = textContainer.layoutOrientation == NSTextLayoutOrientationVertical; - NSRange charRange = [layoutManager characterRangeForGlyphRange:glyphRange + BOOL verticalOrientation = (BOOL)textContainer.layoutOrientation; + NSRange charRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL]; - NSParagraphStyle *style = [layoutManager.textStorage attribute:NSParagraphStyleAttributeName - atIndex:charRange.location - effectiveRange:NULL]; - NSFont *refFont = [layoutManager.textStorage attribute:(NSString *)kCTBaselineReferenceInfoAttributeName - atIndex:charRange.location - effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; - *lineFragmentRect = [textContainer.textView backingAlignedRect:*lineFragmentRect - options:NSAlignAllEdgesNearest]; - *lineFragmentUsedRect = [textContainer.textView backingAlignedRect:*lineFragmentUsedRect - options:NSAlignAllEdgesNearest]; - NSRect lineFragmentAscentRect = *lineFragmentRect; - lineFragmentAscentRect.size.height = lineFragmentUsedRect->origin.y - lineFragmentRect->origin.y + - (verticalOrientation ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); - lineFragmentAscentRect = [textContainer.textView backingAlignedRect:lineFragmentAscentRect - options:NSAlignAllEdgesNearest]; - *baselineOffset = lineFragmentAscentRect.size.height; + NSFont *refFont = [layoutManager.textStorage + attribute:(NSString *)kCTBaselineReferenceInfoAttributeName + atIndex:charRange.location + effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; + NSParagraphStyle *rulerAttrs = [layoutManager.textStorage + attribute:NSParagraphStyleAttributeName + atIndex:charRange.location + effectiveRange:NULL]; + CGFloat lineHeightDelta = lineFragmentUsedRect->size.height - rulerAttrs.minimumLineHeight - rulerAttrs.lineSpacing; + if (ABS(lineHeightDelta) > 0.1) { + lineFragmentUsedRect->size.height = round(lineFragmentUsedRect->size.height - lineHeightDelta); + lineFragmentRect->size.height = round(lineFragmentRect->size.height - lineHeightDelta); + } + *baselineOffset = round(lineFragmentUsedRect->origin.y - lineFragmentRect->origin.y + rulerAttrs.minimumLineHeight * 0.5 + + (verticalOrientation ? 0.0 : refFont.ascender * 0.5 + refFont.descender * 0.5)); return YES; } - (BOOL) layoutManager:(NSLayoutManager *)layoutManager shouldBreakLineByWordBeforeCharacterAtIndex:(NSUInteger)charIndex { - return charIndex <= 1 || [layoutManager.textStorage.string characterAtIndex:charIndex - 1] != '\t'; + return charIndex <= 1 || [layoutManager.textStorage.string + characterAtIndex:charIndex - 1] != '\t'; } - (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager @@ -852,7 +889,7 @@ - (NSControlCharacterAction)layoutManager:(NSLayoutManager *)layoutManager forControlCharacterAtIndex:(NSUInteger)charIndex { if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B && [layoutManager.textStorage attribute:(NSString *)kCTRubyAnnotationAttributeName - atIndex:charIndex + atIndex:charIndex effectiveRange:NULL]) { return NSControlCharacterActionWhitespace; } else { @@ -869,14 +906,15 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager CGFloat width = 0.0; if ([layoutManager.textStorage.string characterAtIndex:charIndex] == 0x8B) { NSRange rubyRange; - id rubyAnnotation = [layoutManager.textStorage attribute:(NSString *)kCTRubyAnnotationAttributeName - atIndex:charIndex - effectiveRange:&rubyRange]; + id rubyAnnotation = [layoutManager.textStorage + attribute:(NSString *)kCTRubyAnnotationAttributeName + atIndex:charIndex + effectiveRange:&rubyRange]; if (rubyAnnotation) { - NSAttributedString *rubyString = [layoutManager.textStorage attributedSubstringFromRange:rubyRange]; + NSAttributedString *rubyString = [layoutManager.textStorage + attributedSubstringFromRange:rubyRange]; CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)rubyString); - CGRect rubyRect = CTLineGetBoundsWithOptions(line, 0); - CFRelease(line); + CGRect rubyRect = CTLineGetBoundsWithOptions((CTLineRef)CFAutorelease(line), 0); NSSize baseSize = rubyString.size; width = MAX(0.0, rubyRect.size.width - baseSize.width); } @@ -886,7 +924,7 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager @end // SquirrelLayoutManager -#pragma mark - Typesetting extensions for TextKit 2 (macOS 12 or lower) +#pragma mark - Typesetting extensions for TextKit 2 (MacOS 12 or higher) API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutFragment : NSTextLayoutFragment @@ -896,19 +934,28 @@ @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { - BOOL verticalOrientation = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; + if (@available(macOS 14.0, *)) { + } else { // in macOS 12 and 13, textLineFragments.typographicBouonds are in textContainer coordinates + point.x += self.layoutFragmentFrame.origin.x; + point.y += self.layoutFragmentFrame.origin.y; + } + BOOL verticalOrientation = (BOOL)self.textLayoutManager.textContainer.layoutOrientation; for (NSTextLineFragment *lineFrag in self.textLineFragments) { - CGContextSaveGState(context); - NSFont *refFont = [lineFrag.attributedString attribute:(NSString *)kCTBaselineReferenceInfoAttributeName - atIndex:lineFrag.characterRange.location - effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; - CGPoint renderOrigin = CGPointMake(NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, - NSMidY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + - (verticalOrientation ? 0.0 : refFont.ascender / 2 + refFont.descender / 2)); - CGPoint deviceRenderOrigin = CGContextConvertPointToDeviceSpace(context, renderOrigin); - renderOrigin = CGContextConvertPointToUserSpace(context, CGPointMake(round(deviceRenderOrigin.x), round(deviceRenderOrigin.y))); + CGRect lineRect = CGRectOffset(lineFrag.typographicBounds, point.x, point.y); + CGFloat baseline = NSMidY(lineRect); + if (!verticalOrientation) { + NSFont *refFont = [lineFrag.attributedString + attribute:(NSString *)kCTBaselineReferenceInfoAttributeName + atIndex:lineFrag.characterRange.location + effectiveRange:NULL][(NSString *)kCTBaselineReferenceFont]; + baseline += refFont.ascender * 0.5 + refFont.descender * 0.5; + } + CGPoint renderOrigin = CGPointMake(NSMinX(lineRect) + lineFrag.glyphOrigin.x, + baseline - lineFrag.glyphOrigin.y); + CGPoint deviceOrigin = CGContextConvertPointToDeviceSpace(context, renderOrigin); + renderOrigin = CGContextConvertPointToUserSpace(context, + CGPointMake(round(deviceOrigin.x), round(deviceOrigin.y))); [lineFrag drawAtPoint:renderOrigin inContext:context]; - CGContextRestoreGState(context); } } @@ -925,17 +972,20 @@ - (BOOL) textLayoutManager:(NSTextLayoutManager *)textLayoutManager shouldBreakLineBeforeLocation:(id)location hyphenating:(BOOL)hyphenating { NSTextContentStorage *contentStorage = textLayoutManager.textContainer.textView.textContentStorage; - NSInteger charIndex = [contentStorage offsetFromLocation:contentStorage.documentRange.location - toLocation:location]; - return charIndex <= 1 || [contentStorage.textStorage.string characterAtIndex:(NSUInteger)charIndex - 1] != '\t'; + NSInteger charIndex = [contentStorage + offsetFromLocation:contentStorage.documentRange.location + toLocation:location]; + return charIndex <= 1 || [contentStorage.textStorage.string + characterAtIndex:(NSUInteger)charIndex - 1] != '\t'; } - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutManager textLayoutFragmentForLocation:(id)location inTextElement:(NSTextElement *)textElement { + NSTextRange *textRange = [[NSTextRange alloc] initWithLocation:location + endLocation:textElement.elementRange.endLocation]; return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement - range:[[NSTextRange alloc] initWithLocation:location - endLocation:textElement.elementRange.endLocation]]; + range:textRange]; } @end // SquirrelTextLayoutManager @@ -965,7 +1015,12 @@ - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(maco - (NSRange)getCharRangeFromTextRange:(NSTextRange *)textRange API_AVAILABLE(macos(12.0)); -- (NSRect)contentRectForRange:(NSRange)range; +- (NSRect)blockRectForRange:(NSRange)range; + +- (void)multilineRectForRange:(NSRange)charRange + leadingRect:(NSRectPointer)leadingRect + bodyRect:(NSRectPointer)bodyRect + trailingRect:(NSRectPointer)trailingRect; - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets candidateRanges:(NSArray *)candidateRanges @@ -976,8 +1031,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets - (void)highlightFunctionButton:(rimeIndex)functionButton; -- (BOOL)convertClickSpot:(NSPoint)spot - toIndex:(NSUInteger *)index; +- (NSUInteger)getIndexFromClickSpot:(NSPoint)spot; @end @@ -996,7 +1050,13 @@ - (BOOL)wantsUpdateLayer { } - (SquirrelAppear)appear { - if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames: + SquirrelPanel *panel = (SquirrelPanel *)self.window; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + NSAppearance *effectiveAppearance = [panel.inputController.client performSelector: + @selector(viewEffectiveAppearance)] ? : NSApp.effectiveAppearance; +#pragma clang diagnostic pop + if ([effectiveAppearance bestMatchFromAppearancesWithNames: @[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { return darkAppear; } @@ -1029,7 +1089,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { textLayoutManager.usesHyphenation = NO; textLayoutManager.delegate = textLayoutManager; NSTextContainer *textContainer = [[NSTextContainer alloc] - initWithSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + initWithSize:NSZeroSize]; textContainer.lineFragmentPadding = 0; textLayoutManager.textContainer = textContainer; NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; @@ -1044,7 +1104,7 @@ - (instancetype)initWithFrame:(NSRect)frameRect { layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; layoutManager.delegate = layoutManager; NSTextContainer *textContainer = [[NSTextContainer alloc] - initWithContainerSize:NSMakeSize(NSViewWidthSizable, CGFLOAT_MAX)]; + initWithContainerSize:NSZeroSize]; textContainer.lineFragmentPadding = 0; [layoutManager addTextContainer:textContainer]; _textStorage = [[NSTextStorage alloc] init]; @@ -1084,10 +1144,12 @@ - (NSRange)getCharRangeFromTextRange:(NSTextRange *)textRange API_AVAILABLE(maco return NSMakeRange(NSNotFound, 0); } else { NSTextContentStorage *contentStorage = _textView.textContentStorage; - NSInteger location = [contentStorage offsetFromLocation:contentStorage.documentRange.location - toLocation:textRange.location]; - NSInteger length = [contentStorage offsetFromLocation:textRange.location - toLocation:textRange.endLocation]; + NSInteger location = [contentStorage + offsetFromLocation:contentStorage.documentRange.location + toLocation:textRange.location]; + NSInteger length = [contentStorage + offsetFromLocation:textRange.location + toLocation:textRange.endLocation]; return NSMakeRange((NSUInteger)location, (NSUInteger)length); } } @@ -1095,166 +1157,84 @@ - (NSRange)getCharRangeFromTextRange:(NSTextRange *)textRange API_AVAILABLE(maco // Get the rectangle containing entire contents, expensive to calculate - (NSRect)contentRect { if (@available(macOS 12.0, *)) { - [_textView.textLayoutManager ensureLayoutForRange:_textView.textContentStorage.documentRange]; + [_textView.textLayoutManager + ensureLayoutForRange:_textView.textContentStorage.documentRange]; return _textView.textLayoutManager.usageBoundsForTextContainer; } else { - [_textView.layoutManager ensureLayoutForTextContainer:_textView.textContainer]; - return [_textView.layoutManager usedRectForTextContainer:_textView.textContainer]; + [_textView.layoutManager + ensureLayoutForTextContainer:_textView.textContainer]; + return [_textView.layoutManager + usedRectForTextContainer:_textView.textContainer]; } } // Get the rectangle containing the range of text, will first convert to glyph or text range, expensive to calculate -- (NSRect)contentRectForRange:(NSRange)range { +- (NSRect)blockRectForRange:(NSRange)range { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromCharRange:range]; - NSRect __block contentRect = NSZeroRect; + NSRect __block blockRect = NSZeroRect; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeHighlight - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, - NSTextContainer *textContainer) { - contentRect = NSUnionRect(contentRect, segFrame); + type:NSTextLayoutManagerSegmentTypeHighlight + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, + CGFloat baseline, NSTextContainer *textContainer) { + blockRect = NSUnionRect(blockRect, segFrame); return YES; }]; CGFloat lineSpacing = [[_textStorage attribute:NSParagraphStyleAttributeName atIndex:NSMaxRange(range) - 1 effectiveRange:NULL] lineSpacing]; - contentRect.size.height += lineSpacing; - return contentRect; + blockRect.size.height += lineSpacing; + return blockRect; } else { NSTextContainer *textContainer = _textView.textContainer; NSLayoutManager *layoutManager = _textView.layoutManager; NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; NSRange firstLineRange = NSMakeRange(NSNotFound, 0); - NSRect firstLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location - effectiveRange:&firstLineRange]; + NSRect firstLineRect = [layoutManager + lineFragmentUsedRectForGlyphAtIndex:glyphRange.location + effectiveRange:&firstLineRange]; if (NSMaxRange(glyphRange) <= NSMaxRange(firstLineRange)) { - CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(firstLineRect); - return NSMakeRect(NSMinX(firstLineRect) + startX, NSMinY(firstLineRect), - endX - startX, NSHeight(firstLineRect)); + CGFloat headX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; + CGFloat tailX = NSMaxRange(glyphRange) < NSMaxRange(firstLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(firstLineRect); + return NSMakeRect(NSMinX(firstLineRect) + headX, + NSMinY(firstLineRect), + tailX - headX, + NSHeight(firstLineRect)); } else { - NSRect finalLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 - effectiveRange:NULL]; - return NSMakeRect(NSMinX(firstLineRect), NSMinY(firstLineRect), - textContainer.size.width, NSMaxY(finalLineRect) - NSMinY(firstLineRect)); + NSRect finalLineRect = [layoutManager + lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 + effectiveRange:NULL]; + return NSMakeRect(NSMinX(firstLineRect), + NSMinY(firstLineRect), + textContainer.size.width, + NSMaxY(finalLineRect) - NSMinY(firstLineRect)); } } } -// Will triger - (void)updateLayer -- (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets - candidateRanges:(NSArray *)candidateRanges - highlightedIndex:(NSUInteger)highlightedIndex - preeditRange:(NSRange)preeditRange - highlightedPreeditRange:(NSRange)highlightedPreeditRange - pagingRange:(NSRange)pagingRange { - _alignmentRectInsets = alignmentRectInsets; - _candidateRanges = candidateRanges; - _highlightedIndex = highlightedIndex; - _preeditRange = preeditRange; - _highlightedPreeditRange = highlightedPreeditRange; - _pagingRange = pagingRange; - _deleteBackPath = preeditRange.length > 0 ? [NSBezierPath bezierPath] : nil; - _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; - _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; - _functionButton = kVoidSymbol; - // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round - [self setNeedsDisplayInRect:self.bounds]; - [self.textView setNeedsDisplayInRect:self.bounds]; -} - -- (void)highlightFunctionButton:(rimeIndex)functionButton { - _functionButton = functionButton; - if (_deleteBackPath && !_deleteBackPath.empty) { - [self setNeedsDisplayInRect:_deleteBackPath.bounds]; - [self.textView setNeedsDisplayInRect:_deleteBackPath.bounds]; - } - if (_pagingPaths.count > 0) { - [self setNeedsDisplayInRect:_pagingPaths[0].bounds]; - [self setNeedsDisplayInRect:_pagingPaths[1].bounds]; - [self.textView setNeedsDisplayInRect:_pagingPaths[0].bounds]; - [self.textView setNeedsDisplayInRect:_pagingPaths[1].bounds]; - } -} - -// Bezier cubic curve, which has continuous roundness -static NSBezierPath * drawRoundedPolygon(NSArray *vertex, CGFloat radius) { - NSBezierPath *path = [NSBezierPath bezierPath]; - if (vertex.count < 1) { - return path; - } - NSPoint previousPoint = vertex.lastObject.pointValue; - NSPoint point = vertex.firstObject.pointValue; - NSPoint nextPoint; - NSPoint startPoint; - NSPoint endPoint = previousPoint; - CGFloat arcRadius; - CGVector diff = CGVectorMake(point.x - previousPoint.x, point.y - previousPoint.y); - if (ABS(diff.dx) >= ABS(diff.dy)) { - endPoint.x += diff.dx / 2; - endPoint.y = point.y; - } else { - endPoint.y += diff.dy / 2; - endPoint.x = point.x; - } - [path moveToPoint:endPoint]; - for (NSUInteger i = 0; i < vertex.count; ++i) { - startPoint = endPoint; - point = vertex[i].pointValue; - nextPoint = vertex[(i + 1) % vertex.count].pointValue; - arcRadius = MIN(radius, MAX(ABS(point.x - startPoint.x), ABS(point.y - startPoint.y))); - endPoint = point; - diff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); - if (ABS(diff.dx) > ABS(diff.dy)) { - endPoint.x += diff.dx / 2; - arcRadius = MIN(arcRadius, ABS(diff.dx) / 2); - endPoint.y = nextPoint.y; - point.y = nextPoint.y; - } else { - endPoint.y += diff.dy / 2; - arcRadius = MIN(arcRadius, ABS(diff.dy) / 2); - endPoint.x = nextPoint.x; - point.x = nextPoint.x; - } - [path appendBezierPathWithArcFromPoint:point toPoint:endPoint radius:arcRadius]; - } - [path closePath]; - return path; -} - -static inline NSArray * rectVertex(NSRect rect) { - return @[@(rect.origin), - @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), - @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), - @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y))]; -} - -static inline BOOL nearEmptyRect(NSRect rect) { - return NSHeight(rect) * NSWidth(rect) < 1; -} - // Calculate 3 boxes containing the text in range. leadingRect and trailingRect are incomplete line rectangle // bodyRect is the complete line fragment in the middle if the range spans no less than one full line -- (void)multilineRectForRange:(NSRange)charRange +- (void)multilineRectForRange:(NSRange)charRange leadingRect:(NSRectPointer)leadingRect bodyRect:(NSRectPointer)bodyRect trailingRect:(NSRectPointer)trailingRect { if (@available(macOS 12.0, *)) { NSTextRange *textRange = [self getTextRangeFromCharRange:charRange]; - NSMutableArray *lineRects = [[NSMutableArray alloc] init]; - NSMutableArray *lineRanges = [[NSMutableArray alloc] init]; + NSMutableArray *lineRects = [[NSMutableArray alloc] init]; + NSMutableArray *lineRanges = [[NSMutableArray alloc] init]; [_textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeHighlight - options:NSTextLayoutManagerSegmentOptionsNone - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, - NSTextContainer *textContainer) { - if (!nearEmptyRect(segFrame)) { - NSRect lastSegFrame = lineRects.count > 0 ? lineRects.lastObject.rectValue : NSZeroRect; + type:NSTextLayoutManagerSegmentTypeHighlight + options:NSTextLayoutManagerSegmentOptionsNone + usingBlock:^(NSTextRange *segRange, CGRect segFrame, + CGFloat baseline, NSTextContainer *textContainer) { + if (!NSIsEmptyRect(segFrame)) { + NSRect lastSegFrame = lineRects.count > 0 ? [lineRects.lastObject rectValue] : NSZeroRect; if (NSMinY(segFrame) < NSMaxY(lastSegFrame)) { segFrame = NSUnionRect(segFrame, lastSegFrame); lineRects[lineRects.count - 1] = [NSValue valueWithRect:segFrame]; @@ -1268,29 +1248,35 @@ - (void)multilineRectForRange:(NSRange)charRange return YES; }]; if (lineRects.count == 1) { - *bodyRect = lineRects[0].rectValue; + *bodyRect = [lineRects[0] rectValue]; } else { CGFloat containerWidth = self.contentRect.size.width; - NSRect leadingLineRect = lineRects.firstObject.rectValue; + NSRect leadingLineRect = [lineRects.firstObject rectValue]; leadingLineRect.size.width = containerWidth - NSMinX(leadingLineRect); - NSRect trailingLineRect = lineRects.lastObject.rectValue; + NSRect trailingLineRect = [lineRects.lastObject rectValue]; if (NSMaxX(trailingLineRect) == NSMaxX(leadingLineRect)) { if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { *bodyRect = NSUnionRect(leadingLineRect, trailingLineRect); } else { *leadingRect = leadingLineRect; - *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, + *bodyRect = NSMakeRect(0.0, + NSMaxY(leadingLineRect), + containerWidth, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { *trailingRect = trailingLineRect; if (NSMinX(leadingLineRect) == NSMinX(trailingLineRect)) { - *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, + *bodyRect = NSMakeRect(0.0, + NSMinY(leadingLineRect), + containerWidth, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { *leadingRect = leadingLineRect; - if (lineRanges.lastObject.location > lineRanges.firstObject.endLocation) { - *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, + if (![lineRanges.lastObject containsLocation:[lineRanges.firstObject endLocation]]) { + *bodyRect = NSMakeRect(0.0, + NSMaxY(leadingLineRect), + containerWidth, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } @@ -1301,42 +1287,60 @@ - (void)multilineRectForRange:(NSRange)charRange NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; NSRange leadingLineRange = NSMakeRange(NSNotFound, 0); - NSRect leadingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location - effectiveRange:&leadingLineRange]; - CGFloat startX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; + NSRect leadingLineRect = [layoutManager + lineFragmentUsedRectForGlyphAtIndex:glyphRange.location + effectiveRange:&leadingLineRange]; + CGFloat headX = [layoutManager locationForGlyphAtIndex:glyphRange.location].x; if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) { - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) ? - [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(leadingLineRect); - *bodyRect = NSMakeRect(startX, NSMinY(leadingLineRect), - endX - startX, NSHeight(leadingLineRect)); + CGFloat tailX = NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(leadingLineRect); + *bodyRect = NSMakeRect(headX, NSMinY(leadingLineRect), + tailX - headX, NSHeight(leadingLineRect)); } else { CGFloat containerWidth = self.contentRect.size.width; NSRange trailingLineRange = NSMakeRange(NSNotFound, 0); - NSRect trailingLineRect = [layoutManager lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 - effectiveRange:&trailingLineRange]; - CGFloat endX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) - ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x : NSWidth(trailingLineRect); + NSRect trailingLineRect = [layoutManager + lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1 + effectiveRange:&trailingLineRange]; + CGFloat tailX = NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange) + ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x + : NSWidth(trailingLineRect); if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) { if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, + *bodyRect = NSMakeRect(0.0, + NSMinY(leadingLineRect), + containerWidth, NSMaxY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), - containerWidth - startX, NSHeight(leadingLineRect)); - *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, + *leadingRect = NSMakeRect(headX, + NSMinY(leadingLineRect), + containerWidth - headX, + NSHeight(leadingLineRect)); + *bodyRect = NSMakeRect(0.0, + NSMaxY(leadingLineRect), + containerWidth, NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect)); } } else { - *trailingRect = NSMakeRect(0.0, NSMinY(trailingLineRect), endX, + *trailingRect = NSMakeRect(0.0, + NSMinY(trailingLineRect), + tailX, NSHeight(trailingLineRect)); if (glyphRange.location == leadingLineRange.location) { - *bodyRect = NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth, + *bodyRect = NSMakeRect(0.0, + NSMinY(leadingLineRect), + containerWidth, NSMinY(trailingLineRect) - NSMinY(leadingLineRect)); } else { - *leadingRect = NSMakeRect(startX, NSMinY(leadingLineRect), - containerWidth - startX, NSHeight(leadingLineRect)); + *leadingRect = NSMakeRect(headX, + NSMinY(leadingLineRect), + containerWidth - headX, + NSHeight(leadingLineRect)); if (trailingLineRange.location > NSMaxRange(leadingLineRange)) { - *bodyRect = NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth, + *bodyRect = NSMakeRect(0.0, + NSMaxY(leadingLineRect), + containerWidth, NSMinY(trailingLineRect) - NSMaxY(leadingLineRect)); } } @@ -1345,6 +1349,99 @@ - (void)multilineRectForRange:(NSRange)charRange } } +// Will triger - (void)updateLayer +- (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets + candidateRanges:(NSArray *)candidateRanges + highlightedIndex:(NSUInteger)highlightedIndex + preeditRange:(NSRange)preeditRange + highlightedPreeditRange:(NSRange)highlightedPreeditRange + pagingRange:(NSRange)pagingRange { + _alignmentRectInsets = alignmentRectInsets; + _candidateRanges = candidateRanges; + _highlightedIndex = highlightedIndex; + _preeditRange = preeditRange; + _highlightedPreeditRange = highlightedPreeditRange; + _pagingRange = pagingRange; + _deleteBackPath = nil; + _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; + _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; + _functionButton = kVoidSymbol; + // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round + [self setNeedsDisplayInRect:self.bounds]; + [self.textView setNeedsDisplayInRect:self.bounds]; +} + +- (void)highlightFunctionButton:(rimeIndex)functionButton { + _functionButton = functionButton; + if (_deleteBackPath) { + [self setNeedsDisplayInRect:_deleteBackPath.bounds]; + [self.textView setNeedsDisplayInRect:_deleteBackPath.bounds]; + } + if (_pagingPaths.count > 0) { + [self setNeedsDisplayInRect:_pagingPaths[0].bounds]; + [self setNeedsDisplayInRect:_pagingPaths[1].bounds]; + [self.textView setNeedsDisplayInRect:_pagingPaths[0].bounds]; + [self.textView setNeedsDisplayInRect:_pagingPaths[1].bounds]; + } +} + +// Bezier cubic curve, which has continuous roundness +static NSBezierPath * squirclePath(NSArray *vertex, CGFloat radius) { + if (vertex.count < 1) { + return nil; + } + NSBezierPath *path = [NSBezierPath bezierPath]; + NSPoint point = vertex.lastObject.pointValue; + NSPoint nextPoint = vertex.firstObject.pointValue; + NSPoint startPoint; + NSPoint endPoint; + NSPoint controlPoint1; + NSPoint controlPoint2; + CGFloat arcRadius; + CGVector nextDiff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); + CGVector lastDiff; + if (ABS(nextDiff.dx) >= ABS(nextDiff.dy)) { + endPoint = NSMakePoint(point.x + nextDiff.dx / 2, nextPoint.y); + } else { + endPoint = NSMakePoint(nextPoint.x, point.y + nextDiff.dy / 2); + } + [path moveToPoint:endPoint]; + for (NSUInteger i = 0; i < vertex.count; ++i) { + lastDiff = nextDiff; + point = nextPoint; + nextPoint = vertex[(i + 1) % vertex.count].pointValue; + nextDiff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); + if (ABS(nextDiff.dx) >= ABS(nextDiff.dy)) { + arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dx), ABS(lastDiff.dy)) / 2); + point.y = nextPoint.y; + startPoint = NSMakePoint(point.x, point.y - copysign(arcRadius, lastDiff.dy)); + controlPoint1 = NSMakePoint(point.x, point.y - copysign(arcRadius * 0.1, lastDiff.dy)); + endPoint = NSMakePoint(point.x + copysign(arcRadius, nextDiff.dx), nextPoint.y); + controlPoint2 = NSMakePoint(point.x + copysign(arcRadius * 0.1, nextDiff.dx), nextPoint.y); + } else { + arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dy), ABS(lastDiff.dx)) / 2); + point.x = nextPoint.x; + startPoint = NSMakePoint(point.x - copysign(arcRadius, lastDiff.dx), point.y); + controlPoint1 = NSMakePoint(point.x - copysign(arcRadius * 0.1, lastDiff.dx), point.y); + endPoint = NSMakePoint(nextPoint.x, point.y + copysign(arcRadius, nextDiff.dy)); + controlPoint2 = NSMakePoint(nextPoint.x, point.y + copysign(arcRadius * 0.1, nextDiff.dy)); + } + [path lineToPoint:startPoint]; + [path curveToPoint:endPoint + controlPoint1:controlPoint1 + controlPoint2:controlPoint2]; + } + [path closePath]; + return path; +} + +static inline NSArray * rectVertex(NSRect rect) { + return @[@(rect.origin), + @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), + @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), + @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y))]; +} + // Based on the 3 boxes from multilineRectForRange, calculate the vertex of the polygon containing the text in range static NSArray * multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && NSIsEmptyRect(trailingRect)) { @@ -1354,23 +1451,25 @@ - (void)multilineRectForRange:(NSRange)charRange } else if (NSIsEmptyRect(leadingRect) && NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { return rectVertex(bodyRect); } else if (NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { - NSArray *leadingVertex = rectVertex(leadingRect); - NSArray *bodyVertex = rectVertex(bodyRect); - return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], bodyVertex[1], bodyVertex[2], leadingVertex[3]]; + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *bodyVertex = rectVertex(bodyRect); + return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], + bodyVertex[1], bodyVertex[2], leadingVertex[3]]; } else if (NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(bodyRect)) { - NSArray *trailingVertex = rectVertex(trailingRect); - NSArray *bodyVertex = rectVertex(bodyRect); - return @[bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], bodyVertex[3]]; + NSArray *trailingVertex = rectVertex(trailingRect); + NSArray *bodyVertex = rectVertex(bodyRect); + return @[bodyVertex[0], trailingVertex[1], trailingVertex[2], + trailingVertex[3], bodyVertex[2], bodyVertex[3]]; } else if (!NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSIsEmptyRect(bodyRect) && NSMinX(leadingRect) <= NSMaxX(trailingRect)) { - NSArray *leadingVertex = rectVertex(leadingRect); - NSArray *trailingVertex = rectVertex(trailingRect); + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *trailingVertex = rectVertex(trailingRect); return @[leadingVertex[0], leadingVertex[1], trailingVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], leadingVertex[2], leadingVertex[3]]; } else if (!NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && !NSIsEmptyRect(bodyRect)) { - NSArray *leadingVertex = rectVertex(leadingRect); - NSArray *bodyVertex = rectVertex(bodyRect); - NSArray *trailingVertex = rectVertex(trailingRect); + NSArray *leadingVertex = rectVertex(leadingRect); + NSArray *bodyVertex = rectVertex(bodyRect); + NSArray *trailingVertex = rectVertex(trailingRect); return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3]]; } else { @@ -1406,38 +1505,38 @@ - (CAShapeLayer *)getFunctionButtonLayer { NSBezierPath *buttonPath; switch (_functionButton) { case kPageUp: - buttonColor = hooverColor(theme.linear ? theme.highlightedStripColor - : theme.highlightedPreeditColor, self.appear); + buttonColor = hooverColor(theme.linear ? theme.highlightedCandidateBackColor + : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[0]; break; case kHome: - buttonColor = disabledColor(theme.linear ? theme.highlightedStripColor - : theme.highlightedPreeditColor, self.appear); + buttonColor = disabledColor(theme.linear ? theme.highlightedCandidateBackColor + : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[0]; break; case kPageDown: - buttonColor = hooverColor(theme.linear ? theme.highlightedStripColor - : theme.highlightedPreeditColor, self.appear); + buttonColor = hooverColor(theme.linear ? theme.highlightedCandidateBackColor + : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[1]; break; case kEnd: - buttonColor = disabledColor(theme.linear ? theme.highlightedStripColor - : theme.highlightedPreeditColor, self.appear); + buttonColor = disabledColor(theme.linear ? theme.highlightedCandidateBackColor + : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[1]; break; case kBackSpace: - buttonColor = hooverColor(theme.highlightedPreeditColor, self.appear); + buttonColor = hooverColor(theme.highlightedPreeditBackColor, self.appear); buttonPath = _deleteBackPath; break; case kEscape: - buttonColor = disabledColor(theme.highlightedPreeditColor, self.appear); + buttonColor = disabledColor(theme.highlightedPreeditBackColor, self.appear); buttonPath= _deleteBackPath; break; default: return nil; break; } - if (!buttonPath.empty && buttonColor) { + if (buttonPath && buttonColor) { CAShapeLayer *functionButtonLayer = [[CAShapeLayer alloc] init]; functionButtonLayer.path = buttonPath.quartzPath; functionButtonLayer.fillColor = buttonColor.CGColor; @@ -1448,18 +1547,15 @@ - (CAShapeLayer *)getFunctionButtonLayer { // All draws happen here - (void)updateLayer { - NSBezierPath *panelPath; - NSBezierPath *backgroundPath; - NSBezierPath *borderPath; - NSBezierPath *highlightedPath; - NSBezierPath *highlightedPreeditPath; - NSBezierPath *candidateBlockPath; - NSBezierPath *gridPath; - SquirrelTheme *theme = self.currentTheme; NSRect panelRect = self.bounds; - NSRect backgroundRect = [self backingAlignedRect:NSInsetRect(panelRect, theme.edgeInset.width, theme.edgeInset.height) + NSRect backgroundRect = [self backingAlignedRect:NSInsetRect(panelRect, theme.borderInset.width, + theme.borderInset.height) options:NSAlignAllEdgesNearest]; + NSBezierPath *panelPath = squirclePath(rectVertex(panelRect), + MIN(theme.cornerRadius, NSHeight(panelRect) * 0.5)); + NSBezierPath *backgroundPath = squirclePath(rectVertex(backgroundRect), + MIN(theme.highlightedCornerRadius, NSHeight(backgroundRect) * 0.5)); NSRange visibleRange; if (@available(macOS 12.0, *)) { @@ -1479,82 +1575,106 @@ - (void)updateLayer { // Draw preedit Rect NSRect preeditRect = NSZeroRect; + NSBezierPath *highlightedPreeditPath; if (preeditRange.length > 0) { - preeditRect = [self contentRectForRange:preeditRange]; - preeditRect.size.width = backgroundRect.size.width; - preeditRect.origin = backgroundRect.origin; - if (candidateBlockRange.length > 0) { - preeditRect.size.height += theme.preeditLinespace; - } + NSRect innerBox = [self blockRectForRange:preeditRange]; + preeditRect = NSMakeRect(backgroundRect.origin.x, + backgroundRect.origin.y, + backgroundRect.size.width, + innerBox.size.height + (candidateBlockRange.length > 0 ? theme.preeditLinespace : 0.0)); preeditRect = [self backingAlignedRect:preeditRect options:NSAlignAllEdgesNearest]; // Draw highlighted part of preedit text NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); - CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, theme.preeditParagraphStyle.maximumLineHeight / 3); - if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditColor) { + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, + theme.preeditParagraphStyle.minimumLineHeight * 0.5); + if (highlightedPreeditRange.length > 0 && theme.highlightedPreeditBackColor) { CGFloat kerning = [theme.preeditAttrs[NSKernAttributeName] doubleValue]; - NSRect innerBox = NSMakeRect(preeditRect.origin.x + ceil(theme.separatorWidth * 0.5) - ceil(kerning * 0.5), preeditRect.origin.y, - preeditRect.size.width - theme.separatorWidth + kerning, preeditRect.size.height); - if (candidateBlockRange.length > 0) { - innerBox.size.height -= theme.preeditLinespace; - } - innerBox = [self backingAlignedRect:innerBox options:NSAlignAllEdgesNearest]; + innerBox.origin.x += _alignmentRectInsets.left - ceil(kerning * 0.5); + innerBox.size.width = backgroundRect.size.width - theme.separatorWidth + kerning; + innerBox.origin.y += _alignmentRectInsets.top; + innerBox = [self backingAlignedRect:innerBox + options:NSAlignAllEdgesNearest]; NSRect leadingRect = NSZeroRect; NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:highlightedPreeditRange leadingRect:&leadingRect - bodyRect:&bodyRect trailingRect:&trailingRect]; - leadingRect = nearEmptyRect(leadingRect) ? NSZeroRect : - [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(leadingRect, - ceil(kerning * 0.5), 0.0), innerBox) - options:NSAlignAllEdgesNearest]; - bodyRect = nearEmptyRect(bodyRect) ? NSZeroRect : - [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(bodyRect, - ceil(kerning * 0.5), 0.0), innerBox) - options:NSAlignAllEdgesNearest]; - trailingRect = nearEmptyRect(trailingRect) ? NSZeroRect : - [self backingAlignedRect:NSIntersectionRect(NSOffsetRect(trailingRect, - ceil(kerning * 0.5), 0.0), innerBox) - options:NSAlignAllEdgesNearest]; + [self multilineRectForRange:highlightedPreeditRange + leadingRect:&leadingRect + bodyRect:&bodyRect + trailingRect:&trailingRect]; + if (!NSIsEmptyRect(leadingRect)) { + leadingRect.origin.x += _alignmentRectInsets.left - ceil(kerning * 0.5); + leadingRect.origin.y += _alignmentRectInsets.top; + leadingRect.size.width += kerning; + leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, innerBox) + options:NSAlignAllEdgesNearest]; + } + if (!NSIsEmptyRect(bodyRect)) { + bodyRect.origin.x += _alignmentRectInsets.left - ceil(kerning * 0.5); + bodyRect.origin.y += _alignmentRectInsets.top; + bodyRect.size.width += kerning; + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, innerBox) + options:NSAlignAllEdgesNearest]; + } + if (!NSIsEmptyRect(trailingRect)) { + trailingRect.origin.x += _alignmentRectInsets.left - ceil(kerning * 0.5); + trailingRect.origin.y += _alignmentRectInsets.top; + trailingRect.size.width += kerning; + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, innerBox) + options:NSAlignAllEdgesNearest]; + } // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - highlightedPreeditPath = drawRoundedPolygon(rectVertex(leadingRect), cornerRadius); - [highlightedPreeditPath appendBezierPath:drawRoundedPolygon(rectVertex(trailingRect), cornerRadius)]; + highlightedPreeditPath = squirclePath(rectVertex(leadingRect), cornerRadius); + [highlightedPreeditPath appendBezierPath:squirclePath(rectVertex(trailingRect), cornerRadius)]; } else { - highlightedPreeditPath = drawRoundedPolygon(multilineRectVertex(leadingRect, bodyRect, trailingRect), cornerRadius); + highlightedPreeditPath = squirclePath(multilineRectVertex(leadingRect, bodyRect, trailingRect), + cornerRadius); } } - NSRect deleteBackRect = [self contentRectForRange:NSMakeRange(NSMaxRange(_preeditRange) - 1, 1)]; + NSRect deleteBackRect = [self blockRectForRange:NSMakeRange(NSMaxRange(_preeditRange) - 1, 1)]; deleteBackRect.size.width += floor(theme.separatorWidth * 0.5); deleteBackRect.origin.x = NSMaxX(backgroundRect) - NSWidth(deleteBackRect); + deleteBackRect.origin.y += _alignmentRectInsets.top; deleteBackRect = [self backingAlignedRect:NSIntersectionRect(deleteBackRect, preeditRect) options:NSAlignAllEdgesNearest]; - _deleteBackPath = drawRoundedPolygon(rectVertex(deleteBackRect), cornerRadius); + _deleteBackPath = squirclePath(rectVertex(deleteBackRect), cornerRadius); } // Draw candidate Rect NSRect candidateBlockRect = NSZeroRect; + NSBezierPath *candidateBlockPath; + NSBezierPath *gridPath; if (candidateBlockRange.length > 0) { - candidateBlockRect = NSInsetRect([self contentRectForRange:candidateBlockRange], 0.0, - theme.linespace * 0.5); + candidateBlockRect = [self blockRectForRange:candidateBlockRange]; candidateBlockRect.size.width = backgroundRect.size.width; - candidateBlockRect.origin = backgroundRect.origin; - if (preeditRange.length != 0) { - candidateBlockRect.origin.y = NSMaxY(preeditRect); - } + candidateBlockRect.origin.x = backgroundRect.origin.x; + candidateBlockRect.origin.y = preeditRange.length == 0 ? NSMinY(backgroundRect) : NSMaxY(preeditRect); if (pagingRange.length == 0 || theme.linear) { candidateBlockRect.size.height = NSMaxY(backgroundRect) - NSMinY(candidateBlockRect); + } else { + candidateBlockRect.size.height += theme.linespace; } candidateBlockRect = [self backingAlignedRect:NSIntersectionRect(candidateBlockRect, backgroundRect) options:NSAlignAllEdgesNearest]; - candidateBlockPath = drawRoundedPolygon(rectVertex(candidateBlockRect), - MIN(theme.highlightedCornerRadius, NSHeight(candidateBlockRect) / 3)); + candidateBlockPath = squirclePath(rectVertex(candidateBlockRect), + MIN(theme.highlightedCornerRadius, NSHeight(candidateBlockRect) * 0.5)); // Draw candidate highlight rect - CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, theme.paragraphStyle.maximumLineHeight / 3); + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, + theme.paragraphStyle.minimumLineHeight * 0.5); if (theme.linear) { - CGFloat gridOriginY = NSMinY(candidateBlockRect); - CGFloat tabInterval = theme.separatorWidth * 2; + CGFloat gridOriginY; + CGFloat tabInterval; CGFloat kerning = [theme.attrs[NSKernAttributeName] doubleValue]; + if (theme.tabled) { + gridPath = [NSBezierPath bezierPath]; + gridOriginY = NSMinY(candidateBlockRect); + tabInterval = theme.separatorWidth * 2; + } for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { NSRange candidateRange = NSIntersectionRange(_candidateRanges[i].rangeValue, visibleRange); if (candidateRange.length == 0) { @@ -1563,62 +1683,60 @@ - (void)updateLayer { NSRect leadingRect = NSZeroRect; NSRect bodyRect = NSZeroRect; NSRect trailingRect = NSZeroRect; - [self multilineRectForRange:candidateRange leadingRect:&leadingRect - bodyRect:&bodyRect trailingRect:&trailingRect]; - if (nearEmptyRect(leadingRect)) { - leadingRect = NSZeroRect; + [self multilineRectForRange:candidateRange + leadingRect:&leadingRect + bodyRect:&bodyRect + trailingRect:&trailingRect]; + if (NSIsEmptyRect(leadingRect)) { bodyRect.origin.y -= ceil(theme.linespace * 0.5); bodyRect.size.height += ceil(theme.linespace * 0.5); } else { - leadingRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); - leadingRect.size.width += theme.separatorWidth + kerning * 0.5; - leadingRect.origin.y -= ceil(theme.linespace * 0.5); + leadingRect.origin.x += theme.borderInset.width; + leadingRect.size.width += theme.separatorWidth; + leadingRect.origin.y += _alignmentRectInsets.top - ceil(theme.linespace * 0.5); leadingRect.size.height += ceil(theme.linespace * 0.5); leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; } - if (nearEmptyRect(trailingRect)) { - trailingRect = NSZeroRect; + if (NSIsEmptyRect(trailingRect)) { bodyRect.size.height += floor(theme.linespace * 0.5); } else { - trailingRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); - trailingRect.size.width += theme.separatorWidth + kerning * 0.5; + trailingRect.origin.x += theme.borderInset.width; + trailingRect.size.width += theme.separatorWidth; + trailingRect.origin.y += _alignmentRectInsets.top; trailingRect.size.height += floor(theme.linespace * 0.5); trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; } - if (nearEmptyRect(bodyRect)) { - bodyRect = NSZeroRect; - } else { - bodyRect.origin.x -= ceil(theme.separatorWidth * 0.5 + kerning * 0.5); - bodyRect.size.width += theme.separatorWidth + kerning * 0.5; + if (!NSIsEmptyRect(bodyRect)) { + bodyRect.origin.x += theme.borderInset.width; + bodyRect.size.width += theme.separatorWidth; + bodyRect.origin.y += _alignmentRectInsets.top; bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; } if (theme.tabled) { CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border - [gridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + theme.separatorWidth * 0.5, bottomEdge)]; - [gridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - theme.separatorWidth * 0.5, bottomEdge)]; - [gridPath closePath]; + [gridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + + theme.separatorWidth * 0.5, bottomEdge)]; + [gridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - + theme.separatorWidth * 0.5, bottomEdge)]; gridOriginY = bottomEdge; } - CGPoint leadOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; - if (leadOrigin.x > NSMinX(candidateBlockRect) +theme.separatorWidth * 0.5) { // vertical bar - [gridPath moveToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + ceil(theme.linespace * 0.5) + - theme.highlightedCornerRadius / 2)]; - [gridPath lineToPoint:NSMakePoint(leadOrigin.x, leadOrigin.y + floor(theme.linespace * 0.5) + - theme.paragraphStyle.maximumLineHeight - theme.highlightedCornerRadius / 2)]; - [gridPath closePath]; + CGPoint headOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; + if (headOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth + kerning) { // vertical bar + [gridPath moveToPoint:NSMakePoint(headOrigin.x, headOrigin.y + cornerRadius * 0.8)]; + [gridPath lineToPoint:NSMakePoint(headOrigin.x, NSMaxY(NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect) - cornerRadius * 0.8)]; } - CGFloat endEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((endEdge + kerning + theme.separatorWidth * 0.5) / tabInterval) * tabInterval - theme.separatorWidth * 0.5; + CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); + CGFloat tabPosition = ceil((tailEdge + kerning * 0.5 - theme.borderInset.width) / tabInterval) * tabInterval + theme.borderInset.width; if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.width += tabPosition - endEdge; + trailingRect.size.width += tabPosition - tailEdge; trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; } else if (NSIsEmptyRect(leadingRect)) { - bodyRect.size.width += tabPosition - endEdge; + bodyRect.size.width += tabPosition - tailEdge; bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; } @@ -1627,10 +1745,10 @@ - (void)updateLayer { // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect) && NSMaxX(trailingRect) < NSMinX(leadingRect)) { - candidatePath = drawRoundedPolygon(rectVertex(leadingRect), cornerRadius); - [candidatePath appendBezierPath:drawRoundedPolygon(rectVertex(trailingRect), cornerRadius)]; + candidatePath = squirclePath(rectVertex(leadingRect), cornerRadius); + [candidatePath appendBezierPath:squirclePath(rectVertex(trailingRect), cornerRadius)]; } else { - candidatePath = drawRoundedPolygon(multilineRectVertex(leadingRect, bodyRect, trailingRect), cornerRadius); + candidatePath = squirclePath(multilineRectVertex(leadingRect, bodyRect, trailingRect), cornerRadius); } _candidatePaths[i] = candidatePath; } @@ -1640,26 +1758,30 @@ - (void)updateLayer { if (candidateRange.length == 0) { break; } - NSRect candidateRect = [self contentRectForRange:candidateRange]; + NSRect candidateRect = [self blockRectForRange:candidateRange]; candidateRect.size.width = backgroundRect.size.width; candidateRect.origin.x = backgroundRect.origin.x; - candidateRect.origin.y -= ceil(theme.linespace * 0.5); + candidateRect.origin.y += _alignmentRectInsets.top - ceil(theme.linespace * 0.5); candidateRect.size.height += theme.linespace; candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, candidateBlockRect) options:NSAlignAllEdgesNearest]; - NSBezierPath *candidatePath = drawRoundedPolygon(rectVertex(candidateRect), cornerRadius); - _candidatePaths[i] = candidatePath; + _candidatePaths[i] = squirclePath(rectVertex(candidateRect), cornerRadius); } } } // Draw paging Rect if (pagingRange.length > 0) { - NSRect pageUpRect = [self contentRectForRange:NSMakeRange(pagingRange.location, 1)]; - NSRect pageDownRect = [self contentRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; + NSRect pageUpRect = [self blockRectForRange: + NSMakeRange(pagingRange.location, 1)]; + NSRect pageDownRect = [self blockRectForRange: + NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; + pageDownRect.origin.x += _alignmentRectInsets.left; pageDownRect.size.width += ceil(theme.separatorWidth * 0.5); - pageUpRect.origin.x -= ceil(theme.separatorWidth * 0.5); + pageDownRect.origin.y += _alignmentRectInsets.top; + pageUpRect.origin.x += theme.borderInset.width; pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented + pageUpRect.origin.y += _alignmentRectInsets.top; if (theme.linear) { pageUpRect.origin.y -= ceil(theme.linespace * 0.5); pageUpRect.size.height += theme.linespace; @@ -1668,79 +1790,81 @@ - (void)updateLayer { pageUpRect = NSIntersectionRect(pageUpRect, candidateBlockRect); pageDownRect = NSIntersectionRect(pageDownRect, candidateBlockRect); } else { - NSRect pagingRect = NSMakeRect(NSMinX(backgroundRect), NSMaxY(candidateBlockRect), - NSWidth(backgroundRect), NSMaxY(backgroundRect) - NSMaxY(candidateBlockRect)); + NSRect pagingRect = NSMakeRect(NSMinX(backgroundRect), + NSMaxY(candidateBlockRect), + NSWidth(backgroundRect), + NSMaxY(backgroundRect) - NSMaxY(candidateBlockRect)); pageUpRect = NSIntersectionRect(pageUpRect, pagingRect); pageDownRect = NSIntersectionRect(pageDownRect, pagingRect); } - pageUpRect = [self backingAlignedRect:pageUpRect options:NSAlignAllEdgesNearest]; - pageDownRect = [self backingAlignedRect:pageDownRect options:NSAlignAllEdgesNearest]; - CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) / 3); - _pagingPaths[0] = drawRoundedPolygon(rectVertex(pageUpRect), cornerRadius); - _pagingPaths[1] = drawRoundedPolygon(rectVertex(pageDownRect), cornerRadius); - } - - // Draw borders - backgroundPath = drawRoundedPolygon(rectVertex(backgroundRect), - MIN(theme.highlightedCornerRadius, NSHeight(backgroundRect) / 3)); - panelPath = drawRoundedPolygon(rectVertex(panelRect), MIN(theme.cornerRadius, NSHeight(panelRect) / 3)); - if (!NSEqualSizes(theme.edgeInset, NSZeroSize)) { - borderPath = [panelPath copy]; - [borderPath appendBezierPath:backgroundPath]; + pageUpRect = [self backingAlignedRect:pageUpRect + options:NSAlignAllEdgesNearest]; + pageDownRect = [self backingAlignedRect:pageDownRect + options:NSAlignAllEdgesNearest]; + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, + MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) * 0.5); + _pagingPaths[0] = squirclePath(rectVertex(pageUpRect), cornerRadius); + _pagingPaths[1] = squirclePath(rectVertex(pageDownRect), cornerRadius); } // Set layers + CIFilter *sourceOutFilter = [CIFilter filterWithName:@"CISourceOutCompositing"]; + [sourceOutFilter setDefaults]; _shape.path = panelPath.quartzPath; _shape.fillColor = NSColor.whiteColor.CGColor; [self.layer setSublayers:nil]; - CALayer *panelLayer = [[CALayer alloc] init]; - if (theme.translucency > 0) { - panelLayer.opacity = 1.0f - (float)theme.translucency; - } + CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; + // border layer + BOOL drawBorders = !NSEqualSizes(theme.borderInset, NSZeroSize) && theme.borderColor; + panelLayer.path = panelPath.quartzPath; + panelLayer.fillColor = drawBorders ? theme.borderColor.CGColor : nil; + panelLayer.opacity = 1.0f - (float)theme.translucency; [self.layer addSublayer:panelLayer]; - // background image (pattern style) layer - if (theme.backgroundImage && theme.backgroundImage.valid) { - CAShapeLayer *backgroundImageLayer = [[CAShapeLayer alloc] init]; - CGAffineTransform transform = theme.vertical ? CGAffineTransformMakeRotation(M_PI_2) : CGAffineTransformIdentity; - transform = CGAffineTransformTranslate(transform, _alignmentRectInsets.left - theme.edgeInset.width, - _alignmentRectInsets.top - theme.edgeInset.height); - backgroundImageLayer.path = CFAutorelease(CGPathCreateCopyByTransformingPath(backgroundPath.quartzPath, &transform)); - backgroundImageLayer.fillColor = [NSColor colorWithPatternImage:theme.backgroundImage].CGColor; - backgroundImageLayer.affineTransform = CGAffineTransformInvert(transform); - [panelLayer addSublayer:backgroundImageLayer]; - } // background color layer CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; backgroundLayer.path = backgroundPath.quartzPath; - backgroundLayer.fillColor = theme.backgroundColor.CGColor; + backgroundLayer.fillColor = theme.backColor.CGColor; + // background image (pattern style) layer + if (theme.backImage.valid) { + CAShapeLayer *backImageLayer = [[CAShapeLayer alloc] init]; + CGAffineTransform transform = theme.vertical ? CGAffineTransformMakeRotation(M_PI_2) + : CGAffineTransformIdentity; + transform = CGAffineTransformTranslate(transform, - backgroundRect.origin.x, + - backgroundRect.origin.y); + backImageLayer.path = (CGPathRef)CFAutorelease(CGPathCreateCopyByTransformingPath + (backgroundPath.quartzPath, &transform)); + backImageLayer.fillColor = [NSColor colorWithPatternImage:theme.backImage].CGColor; + backImageLayer.affineTransform = CGAffineTransformInvert(transform); + backImageLayer.backgroundFilters = drawBorders ? @[sourceOutFilter] : nil; + [panelLayer addSublayer:backImageLayer]; + } else if (drawBorders) { + backgroundLayer.backgroundFilters = @[sourceOutFilter]; + } [panelLayer addSublayer:backgroundLayer]; if ((_preeditRange.length > 0 || (!theme.linear && _pagingRange.length > 0)) && - theme.preeditBackgroundColor) { - backgroundLayer.fillColor = theme.preeditBackgroundColor.CGColor; - if (!candidateBlockPath.empty) { - [backgroundPath appendBezierPath:candidateBlockPath.bezierPathByReversingPath]; - backgroundLayer.path = backgroundPath.quartzPath; - backgroundLayer.fillRule = kCAFillRuleEvenOdd; + theme.preeditBackColor) { + backgroundLayer.fillColor = theme.preeditBackColor.CGColor; + if (candidateBlockPath) { CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; candidateLayer.path = candidateBlockPath.quartzPath; - candidateLayer.fillColor = theme.backgroundColor.CGColor; - [panelLayer addSublayer:candidateLayer]; + candidateLayer.fillColor = theme.backColor.CGColor; + candidateLayer.backgroundFilters = @[sourceOutFilter]; + [backgroundLayer addSublayer:candidateLayer]; } } // highlighted preedit layer - if (theme.highlightedPreeditColor && !highlightedPreeditPath.empty) { + if (highlightedPreeditPath && theme.highlightedPreeditBackColor) { CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = highlightedPreeditPath.quartzPath; - highlightedPreeditLayer.fillColor = theme.highlightedPreeditColor.CGColor; + highlightedPreeditLayer.fillColor = theme.highlightedPreeditBackColor.CGColor; [self.layer addSublayer:highlightedPreeditLayer]; } // highlighted candidate layer - if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { - CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; - highlightedPath = _candidatePaths[_highlightedIndex]; - highlightedLayer.path = highlightedPath.quartzPath; - highlightedLayer.fillColor = theme.highlightedStripColor.CGColor; - [self.layer addSublayer:highlightedLayer]; + if (_highlightedIndex < _candidatePaths.count && theme.highlightedCandidateBackColor) { + CAShapeLayer *highlightedCandidateLayer = [[CAShapeLayer alloc] init]; + highlightedCandidateLayer.path = _candidatePaths[_highlightedIndex].quartzPath; + highlightedCandidateLayer.fillColor = theme.highlightedCandidateBackColor.CGColor; + [self.layer addSublayer:highlightedCandidateLayer]; } // function buttons (page up, page down, backspace) layer if (_functionButton != kVoidSymbol) { @@ -1750,58 +1874,62 @@ - (void)updateLayer { } } // grids (in candidate block) layer - if (theme.tabled) { + if (gridPath) { CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; gridLayer.path = gridPath.quartzPath; - gridLayer.strokeColor = [theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction - ofColor:(self.appear == darkAppear ? NSColor.lightGrayColor : NSColor.blackColor)].CGColor; - gridLayer.lineWidth = theme.edgeInset.height * 0.5; - gridLayer.lineCap = kCALineCapRound; + gridLayer.lineWidth = 1.0; + gridLayer.strokeColor = [[theme.commentAttrs[NSForegroundColorAttributeName] + blendedColorWithFraction:0.5 ofColor:theme.backColor] CGColor]; [panelLayer addSublayer:gridLayer]; } - // border layer - if (!borderPath.empty && theme.borderColor) { - CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; - borderLayer.path = borderPath.quartzPath; - borderLayer.fillColor = theme.borderColor.CGColor; - borderLayer.fillRule = kCAFillRuleEvenOdd; - [panelLayer addSublayer:borderLayer]; - } -} - -- (BOOL)convertClickSpot:(NSPoint)spot - toIndex:(NSUInteger *)index { - if (NSPointInRect(spot, self.bounds)) { - if (_deleteBackPath != nil && [_deleteBackPath containsPoint:spot]) { - *index = kBackSpace; - return YES; + // logo at the beginning for status message + if (preeditRange.length == 0 && candidateBlockRange.length == 0) { + CALayer *logoLayer = [[CALayer alloc] init]; + CGFloat height = [theme.statusAttrs[NSParagraphStyleAttributeName] minimumLineHeight]; + NSRect logoRect = NSMakeRect(backgroundRect.origin.x, + backgroundRect.origin.y, + height, height); + logoLayer.frame = [self backingAlignedRect:NSInsetRect(logoRect, -0.1 * height, -0.1 * height) + options:NSAlignAllEdgesNearest]; + NSImage *logoImage = [NSImage imageNamed:NSImageNameApplicationIcon]; + logoImage.size = logoRect.size; + CGFloat scaleFactor = [logoImage recommendedLayerContentsScale: + [self.window backingScaleFactor]]; + logoLayer.contents = logoImage; + logoLayer.contentsScale = scaleFactor; + logoLayer.affineTransform = theme.vertical ? CGAffineTransformMakeRotation(-M_PI_2) + : CGAffineTransformIdentity; + [self.layer addSublayer:logoLayer]; + } +} + +- (NSUInteger)getIndexFromClickSpot:(NSPoint)spot { + NSPoint point = [self convertPoint:spot fromView:nil]; + if (NSPointInRect(point, self.bounds)) { + if (_deleteBackPath != nil && [_deleteBackPath containsPoint:point]) { + return kBackSpace; } if (_pagingPaths.count > 0) { - if ([_pagingPaths[0] containsPoint:spot]) { - *index = kPageUp; - return YES; + if ([_pagingPaths[0] containsPoint:point]) { + return kPageUp; } - if ([_pagingPaths[1] containsPoint:spot]) { - *index = kPageDown; - return YES; + if ([_pagingPaths[1] containsPoint:point]) { + return kPageDown; } } for (NSUInteger i = 0; i < _candidatePaths.count; ++i) { - if ([_candidatePaths[i] containsPoint:spot]) { - *index = i; - return YES; + if ([_candidatePaths[i] containsPoint:point]) { + return i; } } } - return NO; + return NSNotFound; } @end // SquirrelView - #pragma mark - Panel window, dealing with text content and mouse interactions - @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; @@ -1810,20 +1938,14 @@ @implementation SquirrelPanel { CGFloat _textWidthLimit; NSPoint _scrollLocus; BOOL _initPosition; + NSTimer *_statusTimer; NSUInteger _numCandidates; - NSUInteger _highlighted; + NSUInteger _highlightedIndex; NSUInteger _functionButton; BOOL _caretAtHome; BOOL _firstPage; BOOL _lastPage; - - NSString *_statusMessage; - NSTimer *_statusTimer; -} - -- (BOOL)isFloatingPanel { - return YES; } - (BOOL)linear { @@ -1849,82 +1971,79 @@ - (BOOL)inlineCandidate { - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { SquirrelTheme *theme = [_view selectTheme:appear]; - NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) + NSMutableParagraphStyle *preeditParagraphStyle = [[NSMutableParagraphStyle alloc] init]; + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + NSMutableParagraphStyle *pagingParagraphStyle = [[NSMutableParagraphStyle alloc] init]; + NSMutableParagraphStyle *statusParagraphStyle = [[NSMutableParagraphStyle alloc] init]; + + preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; + statusParagraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; + + preeditParagraphStyle.alignment = NSTextAlignmentLeft; + paragraphStyle.alignment = NSTextAlignmentLeft; + pagingParagraphStyle.alignment = NSTextAlignmentLeft; + statusParagraphStyle.alignment = NSTextAlignmentLeft; + + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left + // characters from setting the writing direction in case the label are direction-less symbols + preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + + NSFont *userFont = [NSFont fontWithDescriptor: + getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; - NSFont *userMonoFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) + NSFont *userMonoFont = [NSFont fontWithDescriptor: + getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) size:kDefaultFontSize]; - NSMutableDictionary *defaultAttrs = [[NSMutableDictionary alloc] init]; - // prevent mac terminal from hijacking non-alphabetic keys on non-inline mode - defaultAttrs[IMKCandidatesSendServerKeyEventFirst] = @(YES); + NSFont *monoDigitFont = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize + weight:NSFontWeightRegular]; - NSMutableDictionary *attrs = defaultAttrs.mutableCopy; + NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; attrs[NSForegroundColorAttributeName] = NSColor.controlTextColor; attrs[NSFontAttributeName] = userFont; - - NSMutableDictionary *highlightedAttrs = defaultAttrs.mutableCopy; - highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; - highlightedAttrs[NSFontAttributeName] = userFont; // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. attrs[NSWritingDirectionAttributeName] = @[@(0)]; - highlightedAttrs[NSWritingDirectionAttributeName] = @[@(0)]; + + NSMutableDictionary *highlightedAttrs = attrs.mutableCopy; + highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; NSMutableDictionary *labelAttrs = attrs.mutableCopy; labelAttrs[NSForegroundColorAttributeName] = NSColor.controlAccentColor; labelAttrs[NSFontAttributeName] = userMonoFont; - NSMutableDictionary *labelHighlightedAttrs = highlightedAttrs.mutableCopy; + NSMutableDictionary *labelHighlightedAttrs = labelAttrs.mutableCopy; labelHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; - labelHighlightedAttrs[NSFontAttributeName] = userMonoFont; - NSMutableDictionary *commentAttrs = defaultAttrs.mutableCopy; + NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; commentAttrs[NSForegroundColorAttributeName] = NSColor.secondaryLabelColor; commentAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *commentHighlightedAttrs = defaultAttrs.mutableCopy; + NSMutableDictionary *commentHighlightedAttrs = commentAttrs.mutableCopy; commentHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; - commentHighlightedAttrs[NSFontAttributeName] = userFont; - NSMutableDictionary *preeditAttrs = defaultAttrs.mutableCopy; + NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; preeditAttrs[NSForegroundColorAttributeName] = NSColor.textColor; preeditAttrs[NSFontAttributeName] = userFont; preeditAttrs[NSLigatureAttributeName] = @(0); + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - NSMutableDictionary *preeditHighlightedAttrs = defaultAttrs.mutableCopy; + NSMutableDictionary *preeditHighlightedAttrs = preeditAttrs.mutableCopy; preeditHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedTextColor; - preeditHighlightedAttrs[NSFontAttributeName] = userFont; - preeditHighlightedAttrs[NSLigatureAttributeName] = @(0); - - NSMutableDictionary *pagingAttrs = defaultAttrs.mutableCopy; - pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? NSColor.controlAccentColor : NSColor.controlTextColor; - NSMutableDictionary *pagingHighlightedAttrs = defaultAttrs.mutableCopy; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear - ? NSColor.alternateSelectedControlTextColor : NSColor.selectedMenuItemTextColor; + NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; + pagingAttrs[NSFontAttributeName] = theme.linear ? userMonoFont : monoDigitFont; + pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? NSColor.controlAccentColor + : NSColor.controlTextColor; - NSMutableParagraphStyle *preeditParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; - NSMutableParagraphStyle *paragraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; - NSMutableParagraphStyle *pagingParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; - NSMutableParagraphStyle *statusParagraphStyle = NSParagraphStyle.defaultParagraphStyle.mutableCopy; - - preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; - preeditParagraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.alignment = NSTextAlignmentLeft; - pagingParagraphStyle.alignment = NSTextAlignmentLeft; - statusParagraphStyle.alignment = NSTextAlignmentLeft; - - // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left - // characters from setting the writing direction in case the label are direction-less symbols - preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + NSMutableDictionary *pagingHighlightedAttrs = pagingAttrs.mutableCopy; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? + NSColor.alternateSelectedControlTextColor : NSColor.selectedMenuItemTextColor; NSMutableDictionary *statusAttrs = commentAttrs.mutableCopy; statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; - preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - [theme setAttrs:attrs highlightedAttrs:highlightedAttrs labelAttrs:labelAttrs @@ -1947,7 +2066,7 @@ - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { } - (instancetype)init { - self = [super initWithContentRect:_position + self = [super initWithContentRect:_IbeamRect styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; @@ -1956,7 +2075,6 @@ - (instancetype)init { self.alphaValue = 1.0; self.hasShadow = NO; self.opaque = NO; - self.displaysWhenScreenProfileChanges = YES; self.backgroundColor = NSColor.clearColor; self.delegate = self; @@ -1976,43 +2094,39 @@ - (instancetype)init { [self updateDisplayParameters]; [self initializeUIStyleForAppearance:defaultAppear]; [self initializeUIStyleForAppearance:darkAppear]; - } - return self; -} -- (void)windowDidChangeScreenProfile:(NSNotification *)notification { - if (self.isVisible) { - [self.inputController.client attributesForCharacterIndex:0 - lineHeightRectangle:&_position]; - [self updateDisplayParameters]; - [self display]; + [self addObserver:self + forKeyPath:@"screen" + options:NSKeyValueObservingOptionNew + context:@selector(updateDisplayParameters)]; } + return self; } -- (void)windowDidChangeScreen:(NSNotification *)notification { - if (self.isVisible) { - [self.inputController.client attributesForCharacterIndex:0 - lineHeightRectangle:&_position]; +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context +{ + if (context == @selector(updateDisplayParameters)) { [self updateDisplayParameters]; - [self display]; } } - (void)sendEvent:(NSEvent *)event { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; + NSUInteger cursorIndex; switch (event.type) { case NSEventTypeLeftMouseUp: - if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _highlighted || cursorIndex == _functionButton) { - [_inputController perform:kSELECT onIndex:cursorIndex]; - } + cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + if (event.clickCount == 1 && cursorIndex != NSNotFound && + (cursorIndex == _highlightedIndex || cursorIndex == _functionButton)) { + [_inputController perform:kSELECT onIndex:cursorIndex]; } break; case NSEventTypeRightMouseUp: - if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _highlighted) { + cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + if (event.clickCount == 1 && cursorIndex != NSNotFound) { + if (cursorIndex == _highlightedIndex) { [_inputController perform:kDELETE onIndex:cursorIndex]; } else if (cursorIndex == _functionButton) { switch (cursorIndex) { @@ -2030,52 +2144,52 @@ - (void)sendEvent:(NSEvent *)event { } break; case NSEventTypeMouseMoved: - if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlighted != cursorIndex) { - _highlighted = cursorIndex; - [_inputController perform:kHILITE onIndex:[_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; - } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || - cursorIndex == kBackSpace) && _functionButton != cursorIndex) { - _functionButton = cursorIndex; - SquirrelTheme *theme = _view.currentTheme; - switch (_functionButton) { - case kPageUp: - [_view.textStorage addAttributes:theme.pagingHighlightedAttrs - range:NSMakeRange(_view.pagingRange.location, 1)]; + cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlightedIndex != cursorIndex) { + _highlightedIndex = cursorIndex; + [_inputController perform:kHILITE onIndex: + [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; + } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || + cursorIndex == kBackSpace) && _functionButton != cursorIndex) { + _functionButton = cursorIndex; + SquirrelTheme *theme = _view.currentTheme; + switch (_functionButton) { + case kPageUp: + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _firstPage ? kHome : kPageUp; + break; + case kPageDown: + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _lastPage ? kEnd : kPageDown; + break; + case kBackSpace: + [_view.textStorage addAttributes:theme.preeditHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + if (_view.pagingRange.length > 0) { [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - if (_view.preeditRange.length > 0) { - [_view.textStorage addAttributes:theme.preeditAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - } - cursorIndex = _firstPage ? kHome : kPageUp; - break; - case kPageDown: - [_view.textStorage addAttributes:theme.pagingAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; - [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + [_view.textStorage addAttributes:theme.pagingAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - if (_view.preeditRange.length > 0) { - [_view.textStorage addAttributes:theme.preeditAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - } - cursorIndex = _lastPage ? kEnd : kPageDown; - break; - case kBackSpace: - [_view.textStorage addAttributes:theme.preeditHighlightedAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - if (_view.pagingRange.length > 0) { - [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(_view.pagingRange.location, 1)]; - [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - } - cursorIndex = _caretAtHome ? kEscape : kBackSpace; - break; - } - [_view highlightFunctionButton:cursorIndex]; - [self display]; + } + cursorIndex = _caretAtHome ? kEscape : kBackSpace; + break; } + [_view highlightFunctionButton:cursorIndex]; + [self display]; } break; case NSEventTypeLeftMouseDragged: @@ -2084,7 +2198,7 @@ - (void)sendEvent:(NSEvent *)event { break; case NSEventTypeScrollWheel: { SquirrelTheme *theme = _view.currentTheme; - CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + + CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] minimumLineHeight] + [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; if (event.phase == NSEventPhaseBegan) { _scrollLocus = NSZeroPoint; @@ -2098,7 +2212,7 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageDown : kPageUp)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { @@ -2106,7 +2220,7 @@ - (void)sendEvent:(NSEvent *)event { onIndex:kPageUp]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageUp : kPageDown)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { @@ -2123,11 +2237,9 @@ - (void)sendEvent:(NSEvent *)event { } - (NSScreen *)screen { - if (!NSIsEmptyRect(_position)) { - for (NSScreen *screen in NSScreen.screens) { - if (NSPointInRect(_position.origin, screen.frame)) { - return screen; - } + for (NSScreen *screen in NSScreen.screens) { + if (NSPointInRect(_IbeamRect.origin, screen.frame)) { + return screen; } } return NSScreen.mainScreen; @@ -2141,8 +2253,10 @@ - (void)updateDisplayParameters { // size limits on textContainer NSRect screenRect = self.screen.visibleFrame; SquirrelTheme *theme = _view.currentTheme; - CGFloat textWidthRatio = MIN(0.5, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - theme.separatorWidth - theme.edgeInset.width * 2 -kOffsetGap; + CGFloat textWidthRatio = MIN(0.8, 1.0 / (theme.vertical ? 4 : 3) + + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + _textWidthLimit = (theme.vertical ? NSHeight(screenRect) : NSWidth(screenRect)) * textWidthRatio - + theme.separatorWidth - theme.borderInset.width * 2; if (theme.lineLength > 0) { _textWidthLimit = MIN(theme.lineLength, _textWidthLimit); } @@ -2150,24 +2264,26 @@ - (void)updateDisplayParameters { CGFloat tabInterval = theme.separatorWidth * 2; _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; } - CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.5 - theme.edgeInset.height * 2 - kOffsetGap; + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.8 - + theme.borderInset.height * 2 - (theme.inlinePreedit ? ceil(theme.linespace * 0.5) : 0.0) - + (theme.linear || !theme.showPaging ? floor(theme.linespace * 0.5) : 0.0); _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, textHeightLimit); - [_view.textView invalidateTextContainerOrigin]; // resize background image, if any - if (theme.backgroundImage && theme.backgroundImage.valid) { - CGFloat panelWidth = _textWidthLimit + theme.separatorWidth; - NSSize backgroundImageSize = theme.backgroundImage.size; - theme.backgroundImage.resizingMode = NSImageResizingModeStretch; - theme.backgroundImage.size = theme.vertical ? - NSMakeSize(backgroundImageSize.width / backgroundImageSize.height * panelWidth, panelWidth) : - NSMakeSize(panelWidth, backgroundImageSize.height / backgroundImageSize.width * panelWidth); + if (theme.backImage.valid) { + CGFloat widthLimit = _textWidthLimit + theme.separatorWidth; + NSSize backImageSize = theme.backImage.size; + theme.backImage.resizingMode = NSImageResizingModeStretch; + theme.backImage.size = theme.vertical + ? NSMakeSize(backImageSize.width / backImageSize.height * widthLimit, widthLimit) + : NSMakeSize(widthLimit, backImageSize.height / backImageSize.width * widthLimit); } } // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { - NSAppearanceName appearanceName = _view.appear == darkAppear ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua; + NSAppearanceName appearanceName = _view.appear == darkAppear ? NSAppearanceNameDarkAqua + : NSAppearanceNameAqua; NSAppearance *requestedAppearance = [NSAppearance appearanceNamed:appearanceName]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; @@ -2177,12 +2293,12 @@ - (void)show { SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; NSEdgeInsets insets = _view.alignmentRectInsets; - CGFloat textWidthRatio = MIN(0.5, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); + CGFloat textWidthRatio = MIN(0.8, 1.0 / (theme.vertical ? 4 : 3) + + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = self.screen.visibleFrame; - CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.5 - insets.top - insets.bottom - kOffsetGap; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position - BOOL sweepVertical = NSWidth(_position) > NSHeight(_position); + BOOL sweepVertical = NSWidth(_IbeamRect) > NSHeight(_IbeamRect); NSRect contentRect = _view.contentRect; NSRect maxContentRect = contentRect; // fixed line length (text width), but not applicable to status message @@ -2193,20 +2309,21 @@ - (void)show { // but only when the text would expand on the side of upstream (i.e. towards the beginning of text) if (theme.rememberSize && _statusMessage == nil) { if (theme.lineLength == 0 && (theme.vertical - ? (sweepVertical ? (NSMinY(_position) - MAX(NSWidth(maxContentRect), _maxSize.width) - insets.right < NSMinY(screenRect)) - : (NSMinY(_position) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect))) - : (sweepVertical ? (NSMinX(_position) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) - : (NSMaxX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect))))) { + ? (sweepVertical ? (NSMinY(_IbeamRect) - MAX(NSWidth(maxContentRect), _maxSize.width) - insets.right < NSMinY(screenRect)) + : (NSMinY(_IbeamRect) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect))) + : (sweepVertical ? (NSMinX(_IbeamRect) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) + : (NSMaxX(_IbeamRect) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect))))) { if (NSWidth(maxContentRect) >= _maxSize.width) { _maxSize.width = NSWidth(maxContentRect); } else { + CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.8 - insets.top - insets.bottom; maxContentRect.size.width = _maxSize.width; textContainer.size = NSMakeSize(_maxSize.width, textHeightLimit); } } CGFloat textHeight = MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom; - if (theme.vertical ? (NSMinX(_position) - textHeight - (sweepVertical ? kOffsetGap : 0) < NSMinX(screenRect)) - : (NSMinY(_position) - textHeight - (sweepVertical ? 0 : kOffsetGap) < NSMinY(screenRect))) { + if (theme.vertical ? (NSMinX(_IbeamRect) - textHeight - (sweepVertical ? kOffsetGap : 0) < NSMinX(screenRect)) + : (NSMinY(_IbeamRect) - textHeight - (sweepVertical ? 0 : kOffsetGap) < NSMinY(screenRect))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { @@ -2226,11 +2343,11 @@ - (void)show { windowRect.size.height = NSHeight(maxContentRect) + insets.top + insets.bottom; } if (sweepVertical) { // vertically centre-align (MidY) in screen coordinates - windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); - windowRect.origin.y = NSMidY(_position) - NSHeight(windowRect) * 0.5; + windowRect.origin.x = NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect); + windowRect.origin.y = NSMidY(_IbeamRect) - NSHeight(windowRect) * 0.5; } else { // horizontally centre-align (MidX) in screen coordinates - windowRect.origin.x = NSMidX(_position) - NSWidth(windowRect) * 0.5; - windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); + windowRect.origin.x = NSMidX(_IbeamRect) - NSWidth(windowRect) * 0.5; + windowRect.origin.y = NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect); } } else { if (theme.vertical) { // anchor is the top right corner in screen coordinates (MaxX, MaxY) @@ -2238,28 +2355,27 @@ - (void)show { NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, NSHeight(maxContentRect) + insets.top + insets.bottom, NSWidth(maxContentRect) + insets.left + insets.right); - _initPosition |= NSIntersectsRect(windowRect, _position); + _initPosition |= NSIntersectsRect(windowRect, _IbeamRect); if (_initPosition) { if (!sweepVertical) { // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect)) { - windowRect.origin.y = NSMaxY(_position) + kOffsetGap; + if (NSMinY(_IbeamRect) - kOffsetGap - NSHeight(screenRect) * textWidthRatio - insets.left - insets.right < NSMinY(screenRect)) { + windowRect.origin.y = NSMaxY(_IbeamRect) + kOffsetGap; } else { - windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect); } // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) + insets.top - NSWidth(windowRect); + windowRect.origin.x = NSMinX(_IbeamRect) + insets.top - NSWidth(windowRect); if (_view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += NSHeight(preeditRect); + windowRect.origin.x += NSHeight([_view blockRectForRange:_view.preeditRange]); } } else { - if (NSMinX(_position) - kOffsetGap - NSWidth(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = NSMaxX(_position) + kOffsetGap; + if (NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect) < NSMinX(screenRect)) { + windowRect.origin.x = NSMaxX(_IbeamRect) + kOffsetGap; } else { - windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); + windowRect.origin.x = NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect); } - windowRect.origin.y = NSMinY(_position) + insets.left - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_IbeamRect) + insets.left - NSHeight(windowRect); } } } else { // anchor is the top left corner in screen coordinates (MinX, MaxY) @@ -2267,45 +2383,43 @@ - (void)show { NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, NSWidth(maxContentRect) + insets.left + insets.right, NSHeight(maxContentRect) + insets.top + insets.bottom); - _initPosition |= NSIntersectsRect(windowRect, _position); + _initPosition |= NSIntersectsRect(windowRect, _IbeamRect); if (_initPosition) { if (sweepVertical) { // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) { - windowRect.origin.x = NSMinX(_position) - kOffsetGap - NSWidth(windowRect); + if (NSMinX(_IbeamRect) - kOffsetGap - NSWidth(screenRect) * textWidthRatio - insets.left - insets.right >= NSMinX(screenRect)) { + windowRect.origin.x = NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect); } else { - windowRect.origin.x = NSMaxX(_position) + kOffsetGap; + windowRect.origin.x = NSMaxX(_IbeamRect) + kOffsetGap; } - windowRect.origin.y = NSMinY(_position) + insets.top - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_IbeamRect) + insets.top - NSHeight(windowRect); if (_view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.y += NSHeight(preeditRect); + windowRect.origin.y += NSHeight([_view blockRectForRange:_view.preeditRange]); } } else { - if (NSMinY(_position) - kOffsetGap - NSHeight(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = NSMaxY(_position) + kOffsetGap; + if (NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect) < NSMinY(screenRect)) { + windowRect.origin.y = NSMaxY(_IbeamRect) + kOffsetGap; } else { - windowRect.origin.y = NSMinY(_position) - kOffsetGap - NSHeight(windowRect); + windowRect.origin.y = NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect); } - windowRect.origin.x = NSMaxX(_position) - insets.left; + windowRect.origin.x = NSMaxX(_IbeamRect) - insets.left; } } } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_position) - kOffsetGap, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_IbeamRect) - kOffsetGap, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_position) + kOffsetGap, NSMinX(screenRect)) : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_IbeamRect) + kOffsetGap, NSMinX(screenRect)) : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_position) + kOffsetGap, NSMinY(screenRect)) : NSMinY(screenRect); + windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_IbeamRect) + kOffsetGap, NSMinY(screenRect)) : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_position) - kOffsetGap, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_IbeamRect) - kOffsetGap, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); } - windowRect = NSIntersectionRect(windowRect, screenRect); if (theme.vertical) { windowRect.origin.x += NSHeight(maxContentRect) - NSHeight(contentRect); @@ -2314,40 +2428,32 @@ - (void)show { windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); } + windowRect = [self.screen backingAlignedRect:NSIntersectionRect(windowRect, screenRect) + options:NSAlignAllEdgesNearest]; + [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! - if (theme.vertical) { - windowRect = [self.screen backingAlignedRect:windowRect options:NSAlignAllEdgesNearest]; - [self setFrame:windowRect display:YES]; - self.contentView.boundsRotation = -90.0; - [self.contentView setBoundsOrigin:NSMakePoint(0.0, NSWidth(windowRect))]; - } else { - windowRect = [self.screen backingAlignedRect:windowRect options:NSAlignAllEdgesNearest]; - [self setFrame:windowRect display:YES]; - self.contentView.boundsRotation = 0.0; - [self.contentView setBoundsOrigin:NSZeroPoint]; - } + self.contentView.boundsRotation = theme.vertical ? -90.0 : 0.0; + [self.contentView setBoundsOrigin:theme.vertical ? NSMakePoint(0.0, NSWidth(windowRect)) : NSZeroPoint]; + NSRect viewRect = self.contentView.bounds; - NSRect textViewRect = NSMakeRect(NSMinX(viewRect) + insets.left - _view.textView.textContainerOrigin.x, - NSMinY(viewRect) + insets.bottom - _view.textView.textContainerOrigin.y, - NSWidth(viewRect) - insets.left - insets.right, - NSHeight(viewRect) - insets.top - insets.bottom); - textViewRect = [self.contentView backingAlignedRect:textViewRect options:NSAlignAllEdgesNearest]; - NSPoint viewOrigin = NSMakePoint(NSMinX(viewRect) - NSMinX(textViewRect), NSMaxY(textViewRect) - NSMaxY(viewRect)); + [_view setBoundsOrigin:NSZeroPoint]; + _view.frame = viewRect; + _view.textView.boundsRotation = 0.0; - [_view setBoundsOrigin:viewOrigin]; [_view.textView setBoundsOrigin:NSZeroPoint]; - _view.frame = viewRect; - _view.textView.frame = textViewRect; + _view.textView.frame = NSOffsetRect(viewRect, insets.left - _view.textView.textContainerOrigin.x, + insets.top - _view.textView.textContainerOrigin.y); if (theme.translucency > 0) { - [_back setBoundsOrigin:viewOrigin]; + [_back setBoundsOrigin:NSZeroPoint]; _back.frame = viewRect; _back.appearance = NSApp.effectiveAppearance; - [_back setHidden:NO]; + _back.hidden = NO; } else { - [_back setHidden:YES]; + _back.hidden = YES; } + self.alphaValue = theme.alpha; [self orderFront:nil]; // reset to initial position after showing status message @@ -2356,7 +2462,7 @@ - (void)show { } - (void)hide { - if (_statusTimer) { + if (_statusTimer.valid) { [_statusTimer invalidate]; _statusTimer = nil; } @@ -2365,6 +2471,12 @@ - (void)hide { _initPosition = YES; } +- (void)dealloc { + [self removeObserver:self + forKeyPath:@"screen" + context:@selector(updateDisplayParameters)]; +} + - (BOOL)shouldBreakLineInsideRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; NSUInteger __block lineCount = 0; @@ -2372,19 +2484,21 @@ - (BOOL)shouldBreakLineInsideRange:(NSRange)range { NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeHighlight - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, - NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeHighlight + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, + CGFloat baseline, NSTextContainer *textContainer) { lineCount += 1 + (NSMaxX(segFrame) > _textWidthLimit); return YES; }]; } else { - NSRange glyphRange = [_view.textView.layoutManager glyphRangeForCharacterRange:range - actualCharacterRange:NULL]; - [_view.textView.layoutManager enumerateLineFragmentsForGlyphRange:glyphRange - usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, - NSRange lineRange, BOOL *stop) { + NSRange glyphRange = [_view.textView.layoutManager + glyphRangeForCharacterRange:range + actualCharacterRange:NULL]; + [_view.textView.layoutManager + enumerateLineFragmentsForGlyphRange:glyphRange + usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, + NSRange lineRange, BOOL *stop) { lineCount += 1 + (NSMaxX(usedRect) > self->_textWidthLimit); }]; } @@ -2403,22 +2517,26 @@ - (BOOL)shouldUseTabInRange:(NSRange)range CGFloat __block rangeEndEdge; [_view.textView.textLayoutManager enumerateTextSegmentsInRange:textRange - type:NSTextLayoutManagerSegmentTypeHighlight - options:NSTextLayoutManagerSegmentOptionsRangeNotRequired - usingBlock:^(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, - NSTextContainer *textContainer) { + type:NSTextLayoutManagerSegmentTypeHighlight + options:NSTextLayoutManagerSegmentOptionsRangeNotRequired + usingBlock:^(NSTextRange *segRange, CGRect segFrame, + CGFloat baseline, NSTextContainer *textContainer) { rangeEndEdge = NSMaxX(segFrame); return YES; }]; - [_view.textView.textLayoutManager ensureLayoutForRange:_view.textView.textContentStorage.documentRange]; + [_view.textView.textLayoutManager + ensureLayoutForRange:_view.textView.textContentStorage.documentRange]; NSRect container = _view.textView.textLayoutManager.usageBoundsForTextContainer; *maxLineLength = MAX(*maxLineLength, MAX(NSMaxX(container), _maxSize.width)); return *maxLineLength > rangeEndEdge; } else { - NSUInteger glyphIndex = [_view.textView.layoutManager glyphIndexForCharacterAtIndex:range.location]; + NSUInteger glyphIndex = [_view.textView.layoutManager + glyphIndexForCharacterAtIndex:range.location]; CGFloat rangeEndEdge = NSMaxX([_view.textView.layoutManager - lineFragmentUsedRectForGlyphAtIndex:glyphIndex effectiveRange:NULL]); - NSRect container = [_view.textView.layoutManager usedRectForTextContainer:_view.textView.textContainer]; + lineFragmentUsedRectForGlyphAtIndex:glyphIndex + effectiveRange:NULL]); + NSRect container = [_view.textView.layoutManager + usedRectForTextContainer:_view.textView.textContainer]; *maxLineLength = MAX(*maxLineLength, MAX(NSMaxX(container), _maxSize.width)); return *maxLineLength > rangeEndEdge; } @@ -2430,18 +2548,18 @@ - (void)showPreedit:(NSString *)preedit caretPos:(NSUInteger)caretPos candidates:(NSArray *)candidates comments:(NSArray *)comments - highlighted:(NSUInteger)highlighted + highlightedIndex:(NSUInteger)highlightedIndex pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage { _numCandidates = candidates.count; - _highlighted = _numCandidates == 0 ? NSNotFound : highlighted; + _highlightedIndex = _numCandidates == 0 ? NSNotFound : highlightedIndex; _caretAtHome = caretPos == NSNotFound || (caretPos == selRange.location && selRange.location == 1); _firstPage = pageNum == 0; _lastPage = lastPage; _functionButton = kVoidSymbol; - if (_numCandidates > 0 || (preedit && preedit.length)) { + if (_numCandidates > 0 || preedit.length > 0) { _statusMessage = nil; - if (_statusTimer) { + if (_statusTimer.valid) { [_statusTimer invalidate]; _statusTimer = nil; } @@ -2449,19 +2567,18 @@ - (void)showPreedit:(NSString *)preedit if (_statusMessage) { [self showStatus:_statusMessage]; _statusMessage = nil; - } else if (!_statusTimer) { + } else if (!_statusTimer.valid) { [self hide]; } return; } - if (!NSIntersectsRect(_position, self.screen.frame)) { + if (!NSIntersectsRect(_IbeamRect, self.screen.frame)) { [self updateDisplayParameters]; } SquirrelTheme *theme = _view.currentTheme; - _view.textView.layoutOrientation = theme.vertical ? - NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + _view.textView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical; if (theme.lineLength > 0) { _maxSize.width = MIN(theme.lineLength, _textWidthLimit); } @@ -2470,9 +2587,11 @@ - (void)showPreedit:(NSString *)preedit [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); - NSMutableArray *candidateRanges = - [[NSMutableArray alloc] initWithCapacity:_numCandidates]; + NSMutableArray *candidateRanges = [[NSMutableArray alloc] + initWithCapacity:_numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); + NSUInteger candidateBlockStart; + NSUInteger lineStart; CGFloat topMargin = preedit ? 0.0 : ceil(theme.linespace * 0.5); CGFloat bottomMargin = _numCandidates > 0 && (theme.linear || !theme.showPaging) ? @@ -2485,26 +2604,28 @@ - (void)showPreedit:(NSString *)preedit if (preedit) { NSMutableAttributedString *preeditLine = [[NSMutableAttributedString alloc] init]; if (selRange.location > 0) { - [preeditLine appendAttributedString: - [[NSAttributedString alloc] initWithString:[preedit substringToIndex:selRange.location] - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringToIndex:selRange.location] + attributes:theme.preeditAttrs]]; } if (selRange.length > 0) { NSUInteger highlightedPreeditStart = preeditLine.length; [preeditLine appendAttributedString:[[NSAttributedString alloc] initWithString:[preedit substringWithRange:selRange] - attributes:theme.preeditHighlightedAttrs]]; + attributes:theme.preeditHighlightedAttrs]]; highlightedPreeditRange = NSMakeRange(highlightedPreeditStart, preeditLine.length - highlightedPreeditStart); } if (NSMaxRange(selRange) < preedit.length) { - [preeditLine appendAttributedString: - [[NSAttributedString alloc] initWithString:[preedit substringFromIndex:NSMaxRange(selRange)] - attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:[[NSAttributedString alloc] + initWithString:[preedit substringFromIndex:NSMaxRange(selRange)] + attributes:theme.preeditAttrs]]; } [preeditLine appendAttributedString:[[NSAttributedString alloc] - initWithString:kFullWidthSpace attributes:theme.preeditAttrs]]; - [preeditLine appendAttributedString:_caretAtHome ? theme.symbolDeleteStroke : theme.symbolDeleteFill]; + initWithString:kFullWidthSpace + attributes:theme.preeditAttrs]]; + [preeditLine appendAttributedString:_caretAtHome ? theme.symbolDeleteStroke + : theme.symbolDeleteFill]; // force caret to be rendered sideways, instead of uprights, in vertical orientation if (caretPos != NSNotFound) { [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@(NO) @@ -2514,45 +2635,50 @@ - (void)showPreedit:(NSString *)preedit [text appendAttributedString:preeditLine]; if (_numCandidates > 0) { - [text appendAttributedString:[[NSAttributedString alloc] - initWithString:@"\n" attributes:theme.preeditAttrs]]; + [text appendAttributedString:[[NSAttributedString alloc] + initWithString:@"\n" + attributes:theme.preeditAttrs]]; } else { - if ([self shouldUseTabInRange:NSMakeRange(preeditLine.length - 2, 2) + if ([self shouldUseTabInRange:NSMakeRange(preeditLine.length - 2, 2) maxLineLength:&maxLineLength]) { if (theme.tabled) { maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; } - [text replaceCharactersInRange:NSMakeRange(preeditLine.length - 2, 1) withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(preeditLine.length - 2, 1) + withString:@"\t"]; NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength options:@{}]]; + location:maxLineLength + options:@{}]]; [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit range:preeditRange]; + value:paragraphStylePreedit + range:preeditRange]; } goto drawPanel; } } // candidate items - NSUInteger candidateBlockStart = text.length; - NSUInteger lineStart = text.length; + candidateBlockStart = text.length; + lineStart = text.length; if (theme.linear) { paragraphStyleCandidate = [theme.paragraphStyle copy]; } for (NSUInteger idx = 0; idx < _numCandidates; ++idx) { // attributed labels are already included in candidateFormats - NSMutableAttributedString *item = (idx == highlighted) ? [theme.candidateHighlightedFormats[idx] mutableCopy] - : [theme.candidateFormats[idx] mutableCopy]; + NSMutableAttributedString *item = (idx == highlightedIndex) ? [theme.candidateHighlightedFormats[idx] mutableCopy] + : [theme.candidateFormats[idx] mutableCopy]; NSRange candidateRange = [item.string rangeOfString:@"%@"]; // get the label size for indent - CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange:NSMakeRange(0, candidateRange.location)].size.width); + CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange: + NSMakeRange(0, candidateRange.location)].size.width); // plug in candidate texts and comments into the template [item replaceCharactersInRange:candidateRange withString:candidates[idx]]; NSRange commentRange = [item.string rangeOfString:kTipSpecifier]; if (comments[idx].length != 0) { - [item replaceCharactersInRange:commentRange + [item replaceCharactersInRange:commentRange withString:[@" " stringByAppendingString:comments[idx]]]; } else { [item deleteCharactersInRange:commentRange]; @@ -2560,12 +2686,14 @@ - (void)showPreedit:(NSString *)preedit [item formatMarkDown]; CGFloat annotationHeight = [item annotateRubyInRange:NSMakeRange(0, item.length) - verticalOrientation:theme.vertical maximumLength:_textWidthLimit]; + verticalOrientation:theme.vertical + maximumLength:_textWidthLimit]; if (annotationHeight * 2 > theme.linespace) { [self setAnnotationHeight:annotationHeight]; paragraphStyleCandidate = theme.paragraphStyle.copy; [text enumerateAttribute:NSParagraphStyleAttributeName - inRange:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart) options:0 + inRange:NSMakeRange(candidateBlockStart, text.length - candidateBlockStart) + options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSParagraphStyle *value, NSRange range, BOOL *stop) { NSMutableParagraphStyle *style = value.mutableCopy; style.paragraphSpacing = annotationHeight; @@ -2594,7 +2722,8 @@ - (void)showPreedit:(NSString *)preedit [text appendAttributedString:item]; if (theme.linear && (ceil(item.size.width) > _textWidthLimit || [self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)])) { - [text replaceCharactersInRange:NSMakeRange(separatorStart, separator.length) withString:@"\n"]; + [text replaceCharactersInRange:NSMakeRange(separatorStart, separator.length) + withString:@"\n"]; lineStart = separatorStart + 1; } } else { // at the start of a new line, no need to determine line break @@ -2603,12 +2732,12 @@ - (void)showPreedit:(NSString *)preedit // for linear layout, middle-truncate candidates that are longer than one line if (theme.linear && ceil(item.size.width) > _textWidthLimit) { if (idx < _numCandidates - 1 || theme.showPaging) { - [text appendAttributedString:[[NSAttributedString alloc] + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; } NSMutableParagraphStyle *paragraphStyleTruncating = paragraphStyleCandidate.mutableCopy; paragraphStyleTruncating.lineBreakMode = NSLineBreakByTruncatingMiddle; - [text addAttribute:NSParagraphStyleAttributeName + [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleTruncating range:NSMakeRange(lineStart, item.length)]; [candidateRanges addObject:[NSValue valueWithRange: @@ -2622,55 +2751,64 @@ - (void)showPreedit:(NSString *)preedit // paging indication if (theme.showPaging) { - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] initWithString: - [NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]; - [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke atIndex:0]; + NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] + attributes:theme.pagingAttrs]; + [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke + atIndex:0]; [paging appendAttributedString:lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill]; [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; [text appendAttributedString:paging]; if (theme.linear) { if ([self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)]) { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 0) withString:@"\n"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 0) + withString:@"\n"]; lineStart = pagingStart; pagingStart += 1; } - if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) + if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength] || lineStart != candidateBlockStart) { if (theme.tabled) { maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; } else { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) withString:@"\t"]; + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) + withString:@"\t"]; } paragraphStyleCandidate = theme.paragraphStyle.mutableCopy; paragraphStyleCandidate.tabStops = @[]; NSUInteger candidateEnd = pagingStart - 1; - CGFloat candidateEndPosition = NSMaxX([_view contentRectForRange: + CGFloat candidateEndPosition = NSMaxX([_view blockRectForRange: NSMakeRange(lineStart, candidateEnd - lineStart)]); for (NSUInteger i = 1; i * tabInterval < candidateEndPosition; ++i) { - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] - initWithTextAlignment:NSTextAlignmentLeft - location:i * tabInterval options:@{}]]; + [paragraphStyleCandidate addTabStop: + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft + location:i * tabInterval + options:@{}]]; } - [paragraphStyleCandidate addTabStop:[[NSTextTab alloc] - initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength options:@{}]]; + [paragraphStyleCandidate addTabStop: + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength + options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate range:NSMakeRange(lineStart, text.length - lineStart)]; + value:paragraphStyleCandidate + range:NSMakeRange(lineStart, text.length - lineStart)]; } else { NSMutableParagraphStyle *paragraphStylePaging = theme.pagingParagraphStyle.mutableCopy; - if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) + if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) maxLineLength:&maxLineLength]) { [text replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1) withString:@"\t"]; [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) withString:@"\t"]; paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter - location:maxLineLength / 2 options:@{}], + location:maxLineLength / 2 + options:@{}], [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength options:@{}]]; + location:maxLineLength + options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging @@ -2679,14 +2817,14 @@ - (void)showPreedit:(NSString *)preedit pagingRange = NSMakeRange(text.length - paging.length, paging.length); } // right-align the backward delete symbol - if (preedit && [self shouldUseTabInRange:NSMakeRange(preeditRange.length - 2, 2) + if (preedit && [self shouldUseTabInRange:NSMakeRange(preeditRange.length - 2, 2) maxLineLength:&maxLineLength]) { [text replaceCharactersInRange:NSMakeRange(preeditRange.length - 2, 1) withString:@"\t"]; NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; - paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] - initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength options:@{}]]; + paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength + options:@{}]]; [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePreedit range:preeditRange]; } @@ -2694,61 +2832,61 @@ - (void)showPreedit:(NSString *)preedit // text done! drawPanel: [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height + topMargin, - theme.edgeInset.width + ceil(theme.separatorWidth * 0.5), - theme.edgeInset.height + bottomMargin, - theme.edgeInset.width + floor(theme.separatorWidth * 0.5)); - self.animationBehavior = caretPos == NSNotFound ? - NSWindowAnimationBehaviorUtilityWindow : NSWindowAnimationBehaviorDefault; + NSEdgeInsets insets = NSEdgeInsetsMake(theme.borderInset.height + topMargin, + theme.borderInset.width + ceil(theme.separatorWidth * 0.5), + theme.borderInset.height + bottomMargin, + theme.borderInset.width + floor(theme.separatorWidth * 0.5)); + _view.textView.textContainerInset = NSMakeSize(theme.borderInset.width + ceil(theme.separatorWidth * 0.5), + theme.borderInset.height + topMargin); + self.animationBehavior = caretPos == NSNotFound ? NSWindowAnimationBehaviorUtilityWindow + : NSWindowAnimationBehaviorDefault; [_view drawViewWithInsets:insets candidateRanges:candidateRanges - highlightedIndex:highlighted + highlightedIndex:highlightedIndex preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange]; [self show]; } -- (void)updateStatusLong:(NSString *)messageLong +- (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort { - SquirrelTheme *theme = _view.currentTheme; - if ([theme.statusMessageType isEqualToString:@"mix"]) { - if (messageShort) { - _statusMessage = messageShort; - } else { + switch (_view.currentTheme.statusMessageType) { + case kStatusMessageTypeMixed: + _statusMessage = messageShort ? : messageLong; + break; + case kStatusMessageTypeLong: _statusMessage = messageLong; - } - } else if ([theme.statusMessageType isEqualToString:@"long"]) { - _statusMessage = messageLong; - } else if ([theme.statusMessageType isEqualToString:@"short"]) { - if (messageShort) { - _statusMessage = messageShort; - } else if (messageLong) { - _statusMessage = [messageLong substringWithRange: - [messageLong rangeOfComposedCharacterSequenceAtIndex:0]]; - } + break; + case kStatusMessageTypeShort: + _statusMessage = messageShort ? : + (messageLong ? [messageLong substringWithRange: + [messageLong rangeOfComposedCharacterSequenceAtIndex:0]] : nil); + break; } } - (void)showStatus:(NSString *)message { SquirrelTheme *theme = _view.currentTheme; - NSEdgeInsets insets = NSEdgeInsetsMake(theme.edgeInset.height, - theme.edgeInset.width + ceil(theme.separatorWidth * 0.5), - theme.edgeInset.height, - theme.edgeInset.width + floor(theme.separatorWidth * 0.5)); - _view.textView.layoutOrientation = theme.vertical ? - NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; + _view.textView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical; NSTextStorage *text = _view.textStorage; [text setAttributedString:[[NSAttributedString alloc] - initWithString:message + initWithString:[NSString stringWithFormat:@"%@ %@", kFullWidthSpace, message] attributes:theme.statusAttrs]]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; + NSEdgeInsets insets = NSEdgeInsetsMake(theme.borderInset.height, + theme.borderInset.width + ceil(theme.separatorWidth * 0.5), + theme.borderInset.height, + theme.borderInset.width + floor(theme.separatorWidth * 0.5)); + _view.textView.textContainerInset = NSMakeSize(theme.borderInset.width + ceil(theme.separatorWidth * 0.5), + theme.borderInset.height); + // disable remember_size and fixed line_length for status messages _initPosition = YES; _maxSize = NSZeroSize; - if (_statusTimer) { + if (_statusTimer.valid) { [_statusTimer invalidate]; } self.animationBehavior = NSWindowAnimationBehaviorUtilityWindow; @@ -2772,63 +2910,59 @@ - (void)hideStatus:(NSTimer *)timer { static inline NSColor * blendColors(NSColor *foregroundColor, NSColor *backgroundColor) { - if (!backgroundColor) { // return foregroundColor; - backgroundColor = NSColor.lightGrayColor; - } return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction - ofColor:backgroundColor] + ofColor:backgroundColor ? : NSColor.lightGrayColor] colorWithAlphaComponent:foregroundColor.alphaComponent]; } static NSFontDescriptor * getFontDescriptor(NSString *fullname) { - if (fullname == nil) { + if (fullname.length == 0) { return nil; } - NSArray *fontNames = [fullname componentsSeparatedByString:@","]; - NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] - initWithCapacity:fontNames.count]; - for (__strong NSString *fontName in fontNames) { - fontName = [fontName stringByTrimmingCharactersInSet: - NSCharacterSet.whitespaceAndNewlineCharacterSet]; - if ([NSFont fontWithName:fontName size:0.0] != nil) { + NSArray *fontNames = [fullname componentsSeparatedByString:@","]; + NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] + initWithCapacity:fontNames.count]; + for (NSString *fontName in fontNames) { + NSFont *font = [NSFont fontWithName:[fontName stringByTrimmingCharactersInSet: + NSCharacterSet.whitespaceAndNewlineCharacterSet] size:0.0]; + if (font != nil) { // If the font name is not valid, NSFontDescriptor will still create something for us. // However, when we draw the actual text, Squirrel will crash if there is any font descriptor // with invalid font name. - NSFontDescriptor *fontDescriptor = - [NSFontDescriptor fontDescriptorWithName:fontName size:0.0]; - NSFontDescriptor *UIFontDescriptor = - [fontDescriptor fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized]; - [validFontDescriptors addObject:([NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil - ? UIFontDescriptor : fontDescriptor)]; + NSFontDescriptor *fontDescriptor = font.fontDescriptor; + NSFontDescriptor *UIFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits: + NSFontDescriptorTraitUIOptimized]; + [validFontDescriptors addObject:[NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil ? + UIFontDescriptor : fontDescriptor]; } } if (validFontDescriptors.count == 0) { return nil; } NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - NSFontDescriptor *emojiFontDescriptor = + NSFontDescriptor *emojiFontDescriptor = [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - NSArray *fallbackDescriptors = - [[validFontDescriptors subarrayWithRange:NSMakeRange(1, validFontDescriptors.count - 1)] - arrayByAddingObject:emojiFontDescriptor]; - NSDictionary *attributes = @{NSFontCascadeListAttribute: fallbackDescriptors}; - return [initialFontDescriptor fontDescriptorByAddingAttributes:attributes]; + NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange: + NSMakeRange(1, validFontDescriptors.count - 1)] + arrayByAddingObject:emojiFontDescriptor]; + return [initialFontDescriptor fontDescriptorByAddingAttributes: + @{NSFontCascadeListAttribute:fallbackDescriptors}]; } static CGFloat getLineHeight(NSFont *font, BOOL vertical) { if (vertical) { font = font.verticalFont; } - CGFloat lineHeight = font.ascender - font.descender; - NSArray *fallbackList = - [font.fontDescriptor objectForKey:NSFontCascadeListAttribute]; + CGFloat lineHeight = ceil(font.ascender - font.descender); + NSArray *fallbackList = [font.fontDescriptor + objectForKey:NSFontCascadeListAttribute]; for (NSFontDescriptor *fallback in fallbackList) { NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback size:font.pointSize]; if (vertical) { fallbackFont = fallbackFont.verticalFont; } - lineHeight = MAX(lineHeight, fallbackFont.ascender - fallbackFont.descender); + lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); } return lineHeight; } @@ -2846,43 +2980,43 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return tallestFont; } -static void updateCandidateListLayout(BOOL *isLinearCandidateList, BOOL *isTabledCandidateList, +static void updateCandidateListLayout(BOOL *isLinear, BOOL *isTabled, SquirrelConfig *config, NSString *prefix) { NSString *candidateListLayout = [config getString: [prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { - *isLinearCandidateList = NO; - *isTabledCandidateList = NO; + *isLinear = NO; + *isTabled = NO; } else if ([candidateListLayout isEqualToString:@"linear"]) { - *isLinearCandidateList = YES; - *isTabledCandidateList = NO; + *isLinear = YES; + *isTabled = NO; } else if ([candidateListLayout isEqualToString:@"tabled"]) { // `tabled` is a derived layout of `linear`; tabled implies linear - *isLinearCandidateList = YES; - *isTabledCandidateList = YES; + *isLinear = YES; + *isTabled = YES; } else { // Deprecated. Not to be confused with text_orientation: horizontal NSNumber *horizontal = [config getOptionalBool: [prefix stringByAppendingString:@"/horizontal"]]; if (horizontal) { - *isLinearCandidateList = horizontal.boolValue; - *isTabledCandidateList = NO; + *isLinear = horizontal.boolValue; + *isTabled = NO; } } } -static void updateTextOrientation(BOOL *isVerticalText, SquirrelConfig *config, NSString *prefix) { +static void updateTextOrientation(BOOL *isVertical, SquirrelConfig *config, NSString *prefix) { NSString *textOrientation = [config getString: [prefix stringByAppendingString:@"/text_orientation"]]; if ([textOrientation isEqualToString:@"horizontal"]) { - *isVerticalText = NO; + *isVertical = NO; } else if ([textOrientation isEqualToString:@"vertical"]) { - *isVerticalText = YES; + *isVertical = YES; } else { NSNumber *vertical = [config getOptionalBool: [prefix stringByAppendingString:@"/vertical"]]; if (vertical) { - *isVerticalText = vertical.boolValue; + *isVertical = vertical.boolValue; } } } @@ -2895,11 +3029,11 @@ - (void)setAnnotationHeight:(CGFloat)height { - (void)loadLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update { SquirrelTheme *theme = [_view selectTheme:defaultAppear]; - [SquirrelPanel updateTheme:theme + [SquirrelPanel updateTheme:theme withLabelConfig:config directUpdate:update]; SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; - [SquirrelPanel updateTheme:darkTheme + [SquirrelPanel updateTheme:darkTheme withLabelConfig:config directUpdate:update]; if (update) { @@ -2911,29 +3045,27 @@ + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update { NSUInteger menuSize = (NSUInteger)[config getInt:@"menu/page_size"] ? : 5; - NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; + NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; - NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; - if (selectLabels) { + NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; + if (selectLabels.count > 0) { for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = selectLabels[i]; } } if (selectKeys) { - if (!selectLabels) { - NSString *keyCaps = [selectKeys.uppercaseString - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; + if (selectLabels.count == 0) { + NSString *keyCaps = [selectKeys.uppercaseString stringByApplyingTransform: + NSStringTransformFullwidthToHalfwidth reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; } } } else { selectKeys = [@"1234567890" substringToIndex:menuSize]; - if (!selectLabels) { - NSString *numerals = [selectKeys - stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth - reverse:YES]; + if (selectLabels.count == 0) { + NSString *numerals = [selectKeys stringByApplyingTransform: + NSStringTransformFullwidthToHalfwidth reverse:YES]; for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; } @@ -2947,10 +3079,10 @@ + (void)updateTheme:(SquirrelTheme *)theme - (void)loadConfig:(SquirrelConfig *)config forAppearance:(SquirrelAppear)appear { SquirrelTheme *theme = [_view selectTheme:appear]; - NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; - [SquirrelPanel updateTheme:theme + NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; + [SquirrelPanel updateTheme:theme withConfig:config - styleOptions:styleOptions + styleOptions:styleOptions forAppearance:appear]; [self updateDisplayParameters]; } @@ -2982,22 +3114,22 @@ + (void)updateTheme:(SquirrelTheme *)theme NSNumber *fontSize = [config getOptionalDouble:@"style/font_point" applyConstraint:pos_round]; NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point" + NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point" applyConstraint:pos_round]; NSString *commentFontName = [config getString:@"style/comment_font_face"]; NSNumber *commentFontSize = [config getOptionalDouble:@"style/comment_font_point" applyConstraint:pos_round]; NSNumber *alpha = [config getOptionalDouble:@"style/alpha" applyConstraint:clamp_uni]; - NSNumber *translucency = [config getOptionalDouble:@"style/translucency" + NSNumber *translucency = [config getOptionalDouble:@"style/translucency" applyConstraint:clamp_uni]; - NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius" + NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius" applyConstraint:positive]; - NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius" + NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius" applyConstraint:positive]; NSNumber *borderHeight = [config getOptionalDouble:@"style/border_height" applyConstraint:pos_ceil]; - NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width" + NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width" applyConstraint:pos_ceil]; NSNumber *lineSpacing = [config getOptionalDouble:@"style/line_spacing" applyConstraint:pos_round]; @@ -3006,62 +3138,65 @@ + (void)updateTheme:(SquirrelTheme *)theme NSNumber *baseOffset = [config getOptionalDouble:@"style/base_offset"]; NSNumber *lineLength = [config getOptionalDouble:@"style/line_length"]; // CHROMATICS - NSColor *backgroundColor; - NSImage *backgroundImage; + NSColor *backColor; NSColor *borderColor; - NSColor *preeditBackgroundColor; - NSColor *candidateLabelColor; - NSColor *highlightedCandidateLabelColor; + NSColor *preeditBackColor; NSColor *textColor; - NSColor *highlightedTextColor; - NSColor *highlightedBackColor; NSColor *candidateTextColor; - NSColor *highlightedCandidateTextColor; - NSColor *highlightedCandidateBackColor; NSColor *commentTextColor; + NSColor *candidateLabelColor; + NSColor *highlightedBackColor; + NSColor *highlightedTextColor; + NSColor *highlightedCandidateBackColor; + NSColor *highlightedCandidateTextColor; NSColor *highlightedCommentTextColor; + NSColor *highlightedCandidateLabelColor; + NSImage *backImage; NSString *colorScheme; if (appear == darkAppear) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString: - [NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; + if ((colorScheme = [config getString:[NSString stringWithFormat: + @"style/%@/color_scheme_dark", option]])) break; } colorScheme = colorScheme ? : [config getString:@"style/color_scheme_dark"]; } if (!colorScheme) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString: - [NSString stringWithFormat:@"style/%@/color_scheme", option]])) break; + if ((colorScheme = [config getString:[NSString stringWithFormat: + @"style/%@/color_scheme", option]])) break; } colorScheme = colorScheme ? : [config getString:@"style/color_scheme"]; } BOOL isNative = !colorScheme || [colorScheme isEqualToString:@"native"]; - NSArray *configPrefixes = isNative - ? [@"style/" stringsByAppendingPaths:styleOptions.allObjects] - : [@[[@"preset_color_schemes/" stringByAppendingString:colorScheme]] - arrayByAddingObjectsFromArray:[@"style/" stringsByAppendingPaths:styleOptions.allObjects]]; + NSArray *configPrefixes = isNative + ? [@"style/" stringsByAppendingPaths:styleOptions.allObjects] + : [@[[@"preset_color_schemes/" stringByAppendingString:colorScheme]] + arrayByAddingObjectsFromArray:[@"style/" stringsByAppendingPaths:styleOptions.allObjects]]; // get color scheme and then check possible overrides from styleSwitcher for (NSString *prefix in configPrefixes) { // CHROMATICS override config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; - backgroundColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backgroundColor; - backgroundImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]] ? : backgroundImage; + backColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backColor; borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; - preeditBackgroundColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackgroundColor; + preeditBackColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackColor; textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]] ? : textColor; - highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]] ? : highlightedTextColor; - highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]] ? : highlightedBackColor; candidateTextColor = [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]] ? : candidateTextColor; - highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_text_color"]] ? : highlightedCandidateTextColor; - highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_back_color"]] ? : highlightedCandidateBackColor; commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]] ? : commentTextColor; - highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_comment_text_color"]] ? : highlightedCommentTextColor; candidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_color"]] ? : candidateLabelColor; + highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]] ? : highlightedBackColor; + highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]] ? : highlightedTextColor; + highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString: + @"/hilited_candidate_back_color"]] ? : highlightedCandidateBackColor; + highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString: + @"/hilited_candidate_text_color"]] ? : highlightedCandidateTextColor; + highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString: + @"/hilited_comment_text_color"]] ? : highlightedCommentTextColor; // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid highlightedCandidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]] ? : [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]] ? : highlightedCandidateLabelColor; + backImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]] ? : backImage; // the following per-color-scheme configurations, if exist, will // override configurations with the same name under the global 'style' section @@ -3079,10 +3214,10 @@ + (void)updateTheme:(SquirrelTheme *)theme fontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"] applyConstraint:pos_round] ? : fontSize; labelFontName = [config getString:[prefix stringByAppendingString:@"/label_font_face"]] ? : labelFontName; - labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"] + labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"] applyConstraint:pos_round] ? : labelFontSize; commentFontName = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]] ? : commentFontName; - commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"] + commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"] applyConstraint:pos_round] ? : commentFontSize; alpha = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"] applyConstraint:clamp_uni] ? : alpha; @@ -3109,37 +3244,38 @@ + (void)updateTheme:(SquirrelTheme *)theme labelFontSize = labelFontSize ? : fontSize; commentFontSize = commentFontSize ? : fontSize; NSDictionary *monoDigitAttrs = @{NSFontFeatureSettingsAttribute: - @[@{NSFontFeatureTypeIdentifierKey: @(kNumberSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kMonospacedNumbersSelector)}, - @{NSFontFeatureTypeIdentifierKey: @(kTextSpacingType), - NSFontFeatureSelectorIdentifierKey: @(kHalfWidthTextSelector)}] }; + @[@{NSFontFeatureTypeIdentifierKey:@(kNumberSpacingType), + NSFontFeatureSelectorIdentifierKey:@(kMonospacedNumbersSelector)}, + @{NSFontFeatureTypeIdentifierKey:@(kTextSpacingType), + NSFontFeatureSelectorIdentifierKey:@(kHalfWidthTextSelector)}]}; NSFontDescriptor *fontDescriptor = getFontDescriptor(fontName); - NSFont *font = [NSFont fontWithDescriptor:(fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0].fontName)) + NSFont *font = [NSFont fontWithDescriptor:fontDescriptor ? : getFontDescriptor([NSFont userFontOfSize:0].fontName) size:fontSize.doubleValue]; NSFontDescriptor *labelFontDescriptor = [(getFontDescriptor(labelFontName) ? : fontDescriptor) fontDescriptorByAddingAttributes:monoDigitAttrs]; - NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor + NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:labelFontSize.doubleValue] : [NSFont monospacedDigitSystemFontOfSize:labelFontSize.doubleValue weight:NSFontWeightRegular]; NSString *labelString = [theme.labels componentsJoinedByString:@""]; labelFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)labelFont, (CFStringRef)labelString, - CFRangeMake(0, (CFIndex)labelString.length), CFSTR("zh"))); + CFRangeMake(0, (CFIndex)labelString.length), CFSTR("zh"))); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); - NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) + NSFont *commentFont = [NSFont fontWithDescriptor:commentFontDescriptor ? : fontDescriptor size:commentFontSize.doubleValue]; - NSFont *pagingFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize.doubleValue + NSFont *pagingFont = [NSFont monospacedDigitSystemFontOfSize:labelFontSize.doubleValue weight:NSFontWeightRegular]; - CGFloat fontHeight = ceil(getLineHeight(font, vertical)); - CGFloat labelFontHeight = ceil(getLineHeight(labelFont, vertical)); - CGFloat commentFontHeight = ceil(getLineHeight(commentFont, vertical)); - CGFloat lineHeight = fmax(fontHeight, fmax(labelFontHeight, commentFontHeight)); - CGFloat separatorWidth = ceil([kFullWidthSpace sizeWithAttributes:@{NSFontAttributeName: commentFont}].width); + CGFloat fontHeight = getLineHeight(font, vertical); + CGFloat labelFontHeight = getLineHeight(labelFont, vertical); + CGFloat commentFontHeight = getLineHeight(commentFont, vertical); + CGFloat lineHeight = MAX(fontHeight, MAX(labelFontHeight, commentFontHeight)); + CGFloat separatorWidth = ceil([kFullWidthSpace sizeWithAttributes: + @{NSFontAttributeName: commentFont}].width); spacing = spacing ? : @(0.0); lineSpacing = lineSpacing ? : @(0.0); @@ -3190,8 +3326,10 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingHighlightedAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *zhFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)font, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); - NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForStringWithLanguage((CTFontRef)commentFont, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); + NSFont *zhFont = CFBridgingRelease(CTFontCreateForStringWithLanguage( + (CTFontRef)font, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); + NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForStringWithLanguage( + (CTFontRef)commentFont, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); NSFont *refFont = getTallestFont(@[zhFont, labelFont, zhCommentFont], vertical); attrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @@ -3202,17 +3340,17 @@ + (void)updateTheme:(SquirrelTheme *)theme @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; labelHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; - commentAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + commentAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; commentHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? refFont.verticalFont : refFont}; - preeditAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + preeditAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? zhFont.verticalFont : zhFont}; preeditHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? zhFont.verticalFont : zhFont}; - pagingAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + pagingAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: linear ? (vertical ? refFont.verticalFont : refFont) : pagingFont}; - pagingHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = + pagingHighlightedAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: linear ? (vertical ? refFont.verticalFont : refFont) : pagingFont}; statusAttrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @{(NSString *)kCTBaselineReferenceFont: vertical ? zhCommentFont.verticalFont : zhCommentFont}; @@ -3233,8 +3371,8 @@ + (void)updateTheme:(SquirrelTheme *)theme vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; statusAttrs[(NSString *)kCTBaselineClassAttributeName] = vertical ? (NSString *)kCTBaselineClassIdeographicCentered : (NSString *)kCTBaselineClassRoman; - pagingAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassRoman; - pagingHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassRoman; + pagingAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassIdeographicCentered; + pagingHighlightedAttrs[(NSString *)kCTBaselineClassAttributeName] = (NSString *)kCTBaselineClassIdeographicCentered; attrs[NSBaselineOffsetAttributeName] = baseOffset; highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; @@ -3254,6 +3392,7 @@ + (void)updateTheme:(SquirrelTheme *)theme commentHighlightedAttrs[NSKernAttributeName] = @(ceil(lineHeight * 0.05)); preeditAttrs[NSKernAttributeName] = @(ceil(fontHeight * 0.05)); preeditHighlightedAttrs[NSKernAttributeName] = @(ceil(fontHeight * 0.05)); + statusAttrs[NSKernAttributeName] = @(ceil(commentFontHeight * 0.05)); preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; preeditHighlightedAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; @@ -3266,38 +3405,39 @@ + (void)updateTheme:(SquirrelTheme *)theme // CHROMATICS refinement translucency = translucency ? : @(0.0); - if (translucency.doubleValue > 0 && (appear == darkAppear ? backgroundColor.luminanceComponent > 0.65 - : backgroundColor.luminanceComponent < 0.55)) { - backgroundColor = [backgroundColor invertLuminanceWithAdjustment:0]; + if (translucency.doubleValue > 0 && !isNative && backColor != nil && + (appear == darkAppear ? backColor.luminanceComponent > 0.65 + : backColor.luminanceComponent < 0.55)) { + backColor = [backColor invertLuminanceWithAdjustment:0]; borderColor = [borderColor invertLuminanceWithAdjustment:0]; - preeditBackgroundColor = [preeditBackgroundColor invertLuminanceWithAdjustment:0]; + preeditBackColor = [preeditBackColor invertLuminanceWithAdjustment:0]; + textColor = [textColor invertLuminanceWithAdjustment:0]; candidateTextColor = [candidateTextColor invertLuminanceWithAdjustment:0]; - highlightedCandidateTextColor = [highlightedCandidateTextColor invertLuminanceWithAdjustment:1]; - highlightedCandidateBackColor = [highlightedCandidateBackColor invertLuminanceWithAdjustment:-1]; - candidateLabelColor = [candidateLabelColor invertLuminanceWithAdjustment:0]; - highlightedCandidateLabelColor = [highlightedCandidateLabelColor invertLuminanceWithAdjustment:1]; commentTextColor = [commentTextColor invertLuminanceWithAdjustment:0]; - highlightedCommentTextColor = [highlightedCommentTextColor invertLuminanceWithAdjustment:1]; - textColor = [textColor invertLuminanceWithAdjustment:0]; - highlightedTextColor = [highlightedTextColor invertLuminanceWithAdjustment:1]; + candidateLabelColor = [candidateLabelColor invertLuminanceWithAdjustment:0]; highlightedBackColor = [highlightedBackColor invertLuminanceWithAdjustment:-1]; + highlightedTextColor = [highlightedTextColor invertLuminanceWithAdjustment:1]; + highlightedCandidateBackColor = [highlightedCandidateBackColor invertLuminanceWithAdjustment:-1]; + highlightedCandidateTextColor = [highlightedCandidateTextColor invertLuminanceWithAdjustment:1]; + highlightedCommentTextColor = [highlightedCommentTextColor invertLuminanceWithAdjustment:1]; + highlightedCandidateLabelColor = [highlightedCandidateLabelColor invertLuminanceWithAdjustment:1]; } - backgroundColor = backgroundColor ? : NSColor.controlBackgroundColor; + backColor = backColor ? : NSColor.controlBackgroundColor; borderColor = borderColor ? : isNative ? NSColor.gridColor : nil; - preeditBackgroundColor = preeditBackgroundColor ? : isNative ? NSColor.windowBackgroundColor : nil; + preeditBackColor = preeditBackColor ? : isNative ? NSColor.windowBackgroundColor : nil; + textColor = textColor ? : NSColor.textColor; candidateTextColor = candidateTextColor ? : NSColor.controlTextColor; - highlightedCandidateTextColor = highlightedCandidateTextColor ? : NSColor.selectedMenuItemTextColor; - highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? NSColor.selectedContentBackgroundColor : nil; - candidateLabelColor = candidateLabelColor ? : isNative ? NSColor.controlAccentColor : - blendColors(highlightedCandidateBackColor, highlightedCandidateTextColor); - highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? - NSColor.alternateSelectedControlTextColor : blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); commentTextColor = commentTextColor ? : NSColor.secondaryLabelColor; - highlightedCommentTextColor = highlightedCommentTextColor ? : NSColor.alternateSelectedControlTextColor; - textColor = textColor ? : NSColor.textColor; - highlightedTextColor = highlightedTextColor ? : NSColor.selectedTextColor; + candidateLabelColor = candidateLabelColor ? : isNative ? NSColor.controlAccentColor + : blendColors(candidateTextColor, backColor); highlightedBackColor = highlightedBackColor ? : isNative ? NSColor.selectedTextBackgroundColor : nil; + highlightedTextColor = highlightedTextColor ? : NSColor.selectedTextColor; + highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? NSColor.selectedContentBackgroundColor : nil; + highlightedCandidateTextColor = highlightedCandidateTextColor ? : NSColor.selectedMenuItemTextColor; + highlightedCommentTextColor = highlightedCommentTextColor ? : NSColor.alternateSelectedControlTextColor; + highlightedCandidateLabelColor = highlightedCandidateLabelColor ? : isNative ? NSColor.alternateSelectedControlTextColor : + blendColors(highlightedCandidateTextColor, highlightedCandidateBackColor); attrs[NSForegroundColorAttributeName] = candidateTextColor; highlightedAttrs[NSForegroundColorAttributeName] = highlightedCandidateTextColor; @@ -3311,18 +3451,18 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingHighlightedAttrs[NSForegroundColorAttributeName] = linear ? highlightedCandidateLabelColor : highlightedTextColor; statusAttrs[NSForegroundColorAttributeName] = commentTextColor; - NSSize edgeInset = vertical ? NSMakeSize(borderHeight.doubleValue, borderWidth.doubleValue) - : NSMakeSize(borderWidth.doubleValue, borderHeight.doubleValue); + NSSize borderInset = vertical ? NSMakeSize(borderHeight.doubleValue, borderWidth.doubleValue) + : NSMakeSize(borderWidth.doubleValue, borderHeight.doubleValue); - [theme setCornerRadius:cornerRadius.doubleValue - highlightedCornerRadius:highlightedCornerRadius.doubleValue + [theme setCornerRadius:MIN(cornerRadius.doubleValue, lineHeight * 0.5) + highlightedCornerRadius:MIN(highlightedCornerRadius.doubleValue, lineHeight * 0.5) separatorWidth:separatorWidth - edgeInset:edgeInset + borderInset:borderInset linespace:lineSpacing.doubleValue preeditLinespace:spacing.doubleValue alpha:alpha ? alpha.doubleValue : 1.0 translucency:translucency.doubleValue - lineLength:lineLength && lineLength.doubleValue > 0 ? fmax(ceil(lineLength.doubleValue), separatorWidth * 5) : 0.0 + lineLength:lineLength.doubleValue > 0 ? MAX(ceil(lineLength.doubleValue), separatorWidth * 5) : 0.0 showPaging:showPaging.boolValue rememberSize:rememberSize.boolValue tabled:tabled @@ -3348,12 +3488,12 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingParagraphStyle:pagingParagraphStyle statusParagraphStyle:statusParagraphStyle]; - [theme setBackgroundColor:backgroundColor - backgroundImage:backgroundImage - highlightedStripColor:highlightedCandidateBackColor - highlightedPreeditColor:highlightedBackColor - preeditBackgroundColor:preeditBackgroundColor - borderColor:borderColor]; + [theme setBackColor:backColor + highlightedCandidateBackColor:highlightedCandidateBackColor + highlightedPreeditBackColor:highlightedBackColor + preeditBackColor:preeditBackColor + borderColor:borderColor + backImage:backImage]; [theme setCandidateFormat:candidateFormat ? : kDefaultCandidateFormat]; [theme setStatusMessageType:statusMessageType]; diff --git a/input_source.m b/input_source.m index fd8833c79..d87176312 100644 --- a/input_source.m +++ b/input_source.m @@ -10,28 +10,27 @@ static const CFStringRef kCantInputModeID = CFSTR("im.rime.inputmethod.Squirrel.Cant"); -typedef enum RimeInputMode : int { +typedef NS_OPTIONS(int, RimeInputMode) { DEFAULT_INPUT_MODE = 1 << 0, HANS_INPUT_MODE = 1 << 0, HANT_INPUT_MODE = 1 << 1, CANT_INPUT_MODE = 1 << 2 -} RimeInputMode; +}; void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation (NULL, (UInt8 *)kInstallLocation, (CFIndex)strlen(kInstallLocation), false); if (installedLocationURL) { - TISRegisterInputSource(installedLocationURL); - CFRelease(installedLocationURL); + TISRegisterInputSource((CFURLRef)CFAutorelease(installedLocationURL)); NSLog(@"Registered input source from %s", kInstallLocation); } } -void ActivateInputSource(RimeInputMode modes) { +void ActivateInputSource(int modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); - CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if ((!CFStringCompare(sourceID, kHansInputModeID, 0) && (modes & HANS_INPUT_MODE)) || (!CFStringCompare(sourceID, kHantInputModeID, 0) && (modes & HANT_INPUT_MODE)) || @@ -61,7 +60,7 @@ void DeactivateInputSource(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = CFArrayGetCount(sourceList); i > 0; --i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i - 1); - CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if (!CFStringCompare(sourceID, kHansInputModeID, 0) || !CFStringCompare(sourceID, kHantInputModeID, 0) || @@ -86,7 +85,7 @@ RimeInputMode GetEnabledInputModes(void) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); - CFStringRef sourceID = TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); + CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(inputSource, kTISPropertyInputSourceID); //NSLog(@"Examining input source: %@", sourceID); if (!CFStringCompare(sourceID, kHansInputModeID, 0) || !CFStringCompare(sourceID, kHantInputModeID, 0) || @@ -111,8 +110,8 @@ RimeInputMode GetEnabledInputModes(void) { input_modes & HANT_INPUT_MODE ? " Hant" : "", input_modes & CANT_INPUT_MODE ? " Cant" : ""); } else { - NSArray *languages = - [NSBundle preferredLocalizationsFromArray:@[@"zh-Hans", @"zh-Hant", @"zh-HK"]]; + NSArray *languages = [NSBundle preferredLocalizationsFromArray: + @[@"zh-Hans", @"zh-Hant", @"zh-HK"]]; if (languages.count > 0) { NSString *lang = languages.firstObject; if ([lang isEqualToString:@"zh-Hans"]) { diff --git a/macos_keycode.h b/macos_keycode.h index 51fb37247..ccfcb8c22 100644 --- a/macos_keycode.h +++ b/macos_keycode.h @@ -1,239 +1,27 @@ #ifndef _MACOS_KEYCODE_H_ #define _MACOS_KEYCODE_H_ -// masks -#define OSX_CAPITAL_MASK 1 << 16 -#define OSX_SHIFT_MASK 1 << 17 -#define OSX_CTRL_MASK 1 << 18 -#define OSX_ALT_MASK 1 << 19 -#define OSX_COMMAND_MASK 1 << 20 -#define OSX_FN_MASK 1 << 23 +#import -// key codes -// // credit goes to tekezo@ // https://github.com/tekezo/Karabiner/blob/master/src/bridge/generator/keycode/data/KeyCode.data -// ---------------------------------------- -// alphabet - -#define OSX_VK_A 0x0 -#define OSX_VK_B 0xb -#define OSX_VK_C 0x8 -#define OSX_VK_D 0x2 -#define OSX_VK_E 0xe -#define OSX_VK_F 0x3 -#define OSX_VK_G 0x5 -#define OSX_VK_H 0x4 -#define OSX_VK_I 0x22 -#define OSX_VK_J 0x26 -#define OSX_VK_K 0x28 -#define OSX_VK_L 0x25 -#define OSX_VK_M 0x2e -#define OSX_VK_N 0x2d -#define OSX_VK_O 0x1f -#define OSX_VK_P 0x23 -#define OSX_VK_Q 0xc -#define OSX_VK_R 0xf -#define OSX_VK_S 0x1 -#define OSX_VK_T 0x11 -#define OSX_VK_U 0x20 -#define OSX_VK_V 0x9 -#define OSX_VK_W 0xd -#define OSX_VK_X 0x7 -#define OSX_VK_Y 0x10 -#define OSX_VK_Z 0x6 - -// ---------------------------------------- -// number - -#define OSX_VK_KEY_0 0x1d -#define OSX_VK_KEY_1 0x12 -#define OSX_VK_KEY_2 0x13 -#define OSX_VK_KEY_3 0x14 -#define OSX_VK_KEY_4 0x15 -#define OSX_VK_KEY_5 0x17 -#define OSX_VK_KEY_6 0x16 -#define OSX_VK_KEY_7 0x1a -#define OSX_VK_KEY_8 0x1c -#define OSX_VK_KEY_9 0x19 - -// ---------------------------------------- -// symbol - -// BACKQUOTE is also known as grave accent or backtick. -#define OSX_VK_BACKQUOTE 0x32 -#define OSX_VK_BACKSLASH 0x2a -#define OSX_VK_BRACKET_LEFT 0x21 -#define OSX_VK_BRACKET_RIGHT 0x1e -#define OSX_VK_COMMA 0x2b -#define OSX_VK_DOT 0x2f -#define OSX_VK_EQUAL 0x18 -#define OSX_VK_MINUS 0x1b -#define OSX_VK_QUOTE 0x27 -#define OSX_VK_SEMICOLON 0x29 -#define OSX_VK_SLASH 0x2c - -// ---------------------------------------- -// keypad - -#define OSX_VK_KEYPAD_0 0x52 -#define OSX_VK_KEYPAD_1 0x53 -#define OSX_VK_KEYPAD_2 0x54 -#define OSX_VK_KEYPAD_3 0x55 -#define OSX_VK_KEYPAD_4 0x56 -#define OSX_VK_KEYPAD_5 0x57 -#define OSX_VK_KEYPAD_6 0x58 -#define OSX_VK_KEYPAD_7 0x59 -#define OSX_VK_KEYPAD_8 0x5b -#define OSX_VK_KEYPAD_9 0x5c -#define OSX_VK_KEYPAD_CLEAR 0x47 -#define OSX_VK_KEYPAD_COMMA 0x5f -#define OSX_VK_KEYPAD_DOT 0x41 -#define OSX_VK_KEYPAD_EQUAL 0x51 -#define OSX_VK_KEYPAD_MINUS 0x4e -#define OSX_VK_KEYPAD_MULTIPLY 0x43 -#define OSX_VK_KEYPAD_PLUS 0x45 -#define OSX_VK_KEYPAD_SLASH 0x4b - -// ---------------------------------------- -// special - -#define OSX_VK_DELETE 0x33 -#define OSX_VK_ENTER 0x4c -#define OSX_VK_ENTER_POWERBOOK 0x34 -#define OSX_VK_ESCAPE 0x35 -#define OSX_VK_FORWARD_DELETE 0x75 -#define OSX_VK_HELP 0x72 -#define OSX_VK_RETURN 0x24 -#define OSX_VK_SPACE 0x31 -#define OSX_VK_TAB 0x30 - -// ---------------------------------------- -// function -#define OSX_VK_F1 0x7a -#define OSX_VK_F2 0x78 -#define OSX_VK_F3 0x63 -#define OSX_VK_F4 0x76 -#define OSX_VK_F5 0x60 -#define OSX_VK_F6 0x61 -#define OSX_VK_F7 0x62 -#define OSX_VK_F8 0x64 -#define OSX_VK_F9 0x65 -#define OSX_VK_F10 0x6d -#define OSX_VK_F11 0x67 -#define OSX_VK_F12 0x6f -#define OSX_VK_F13 0x69 -#define OSX_VK_F14 0x6b -#define OSX_VK_F15 0x71 -#define OSX_VK_F16 0x6a -#define OSX_VK_F17 0x40 -#define OSX_VK_F18 0x4f -#define OSX_VK_F19 0x50 - -// ---------------------------------------- -// functional - -#define OSX_VK_BRIGHTNESS_DOWN 0x91 -#define OSX_VK_BRIGHTNESS_UP 0x90 -#define OSX_VK_DASHBOARD 0x82 -#define OSX_VK_EXPOSE_ALL 0xa0 -#define OSX_VK_LAUNCHPAD 0x83 -#define OSX_VK_MISSION_CONTROL 0xa0 - -// ---------------------------------------- -// cursor - -#define OSX_VK_CURSOR_UP 0x7e -#define OSX_VK_CURSOR_DOWN 0x7d -#define OSX_VK_CURSOR_LEFT 0x7b -#define OSX_VK_CURSOR_RIGHT 0x7c - -#define OSX_VK_PAGEUP 0x74 -#define OSX_VK_PAGEDOWN 0x79 -#define OSX_VK_HOME 0x73 -#define OSX_VK_END 0x77 - -// ---------------------------------------- -// modifiers -#define OSX_VK_CAPSLOCK 0x39 -#define OSX_VK_COMMAND_L 0x37 -#define OSX_VK_COMMAND_R 0x36 -#define OSX_VK_CONTROL_L 0x3b -#define OSX_VK_CONTROL_R 0x3e -#define OSX_VK_FN 0x3f -#define OSX_VK_OPTION_L 0x3a -#define OSX_VK_OPTION_R 0x3d -#define OSX_VK_SHIFT_L 0x38 -#define OSX_VK_SHIFT_R 0x3c - -// ---------------------------------------- // pc keyboard -#define OSX_VK_PC_APPLICATION 0x6e -#define OSX_VK_PC_BS 0x33 -#define OSX_VK_PC_DEL 0x75 -#define OSX_VK_PC_INSERT 0x72 -#define OSX_VK_PC_KEYPAD_NUMLOCK 0x47 -#define OSX_VK_PC_PAUSE 0x71 -#define OSX_VK_PC_POWER 0x7f -#define OSX_VK_PC_PRINTSCREEN 0x69 -#define OSX_VK_PC_SCROLLLOCK 0x6b - -// ---------------------------------------- -// international - -#define OSX_VK_DANISH_DOLLAR 0xa -#define OSX_VK_DANISH_LESS_THAN 0x32 - -#define OSX_VK_FRENCH_DOLLAR 0x1e -#define OSX_VK_FRENCH_EQUAL 0x2c -#define OSX_VK_FRENCH_HAT 0x21 -#define OSX_VK_FRENCH_MINUS 0x18 -#define OSX_VK_FRENCH_RIGHT_PAREN 0x1b - -#define OSX_VK_GERMAN_CIRCUMFLEX 0xa -#define OSX_VK_GERMAN_LESS_THAN 0x32 -#define OSX_VK_GERMAN_PC_LESS_THAN 0x80 -#define OSX_VK_GERMAN_QUOTE 0x18 -#define OSX_VK_GERMAN_A_UMLAUT 0x27 -#define OSX_VK_GERMAN_O_UMLAUT 0x29 -#define OSX_VK_GERMAN_U_UMLAUT 0x21 - -#define OSX_VK_ITALIAN_BACKSLASH 0xa -#define OSX_VK_ITALIAN_LESS_THAN 0x32 - -#define OSX_VK_JIS_ATMARK 0x21 -#define OSX_VK_JIS_BRACKET_LEFT 0x1e -#define OSX_VK_JIS_BRACKET_RIGHT 0x2a -#define OSX_VK_JIS_COLON 0x27 -#define OSX_VK_JIS_DAKUON 0x21 -#define OSX_VK_JIS_EISUU 0x66 -#define OSX_VK_JIS_HANDAKUON 0x1e -#define OSX_VK_JIS_HAT 0x18 -#define OSX_VK_JIS_KANA 0x68 -#define OSX_VK_JIS_PC_HAN_ZEN 0x32 -#define OSX_VK_JIS_UNDERSCORE 0x5e -#define OSX_VK_JIS_YEN 0x5d - -#define OSX_VK_RUSSIAN_PARAGRAPH 0xa -#define OSX_VK_RUSSIAN_TILDE 0x32 - -#define OSX_VK_SPANISH_LESS_THAN 0x32 -#define OSX_VK_SPANISH_ORDINAL_INDICATOR 0xa - -#define OSX_VK_SWEDISH_LESS_THAN 0x32 -#define OSX_VK_SWEDISH_SECTION 0xa - -#define OSX_VK_SWISS_LESS_THAN 0x32 -#define OSX_VK_SWISS_SECTION 0xa - -#define OSX_VK_UK_SECTION 0xa +#define kVK_PC_Application 0x6e +#define kVK_PC_BS 0x33 +#define kVK_PC_Del 0x75 +#define kVK_PC_Insert 0x72 +#define kVK_PC_KeypadNumLock 0x47 +#define kVK_PC_Pause 0x71 +#define kVK_PC_Power 0x7f +#define kVK_PC_PrintScreen 0x69 +#define kVK_PC_ScrollLock 0x6b // conversion functions -int osx_modifiers_to_rime_modifiers(unsigned long modifiers); -int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps); +int get_rime_modifiers(NSEventModifierFlags modifiers); +int get_rime_keycode(ushort keycode, unichar keychar, bool shift, bool caps); #endif /* _MACOS_KEYCODE_H_ */ diff --git a/macos_keycode.m b/macos_keycode.m index d44547ee7..94811f489 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -1,130 +1,212 @@ #import "macos_keycode.h" #import +#import - -int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { +int get_rime_modifiers(NSEventModifierFlags modifiers) { int ret = 0; - if (modifiers & OSX_CAPITAL_MASK) + if (modifiers & NSEventModifierFlagCapsLock) ret |= kLockMask; - if (modifiers & OSX_SHIFT_MASK) + if (modifiers & NSEventModifierFlagShift) ret |= kShiftMask; - if (modifiers & OSX_CTRL_MASK) + if (modifiers & NSEventModifierFlagControl) ret |= kControlMask; - if (modifiers & OSX_ALT_MASK) + if (modifiers & NSEventModifierFlagOption) ret |= kAltMask; - if (modifiers & OSX_COMMAND_MASK) + if (modifiers & NSEventModifierFlagCommand) ret |= kSuperMask; - if (modifiers & OSX_FN_MASK) + if (modifiers & NSEventModifierFlagFunction) ret |= kHyperMask; return ret; } -static struct keycode_mapping_t { - int osx_keycode, rime_keycode; -} keycode_mappings[] = { +struct mapping_t { + int from_osx; + int to_rime; +}; + +static struct mapping_t keycode_mappings[] = { // modifiers - { OSX_VK_CAPSLOCK, XK_Caps_Lock }, - { OSX_VK_COMMAND_L, XK_Super_L }, // XK_Meta_L? - { OSX_VK_COMMAND_R, XK_Super_R }, // XK_Meta_R? - { OSX_VK_CONTROL_L, XK_Control_L }, - { OSX_VK_CONTROL_R, XK_Control_R }, - { OSX_VK_FN, XK_Hyper_L }, - { OSX_VK_OPTION_L, XK_Alt_L }, - { OSX_VK_OPTION_R, XK_Alt_R }, - { OSX_VK_SHIFT_L, XK_Shift_L }, - { OSX_VK_SHIFT_R, XK_Shift_R }, + { kVK_CapsLock, XK_Caps_Lock }, + { kVK_Command, XK_Super_L }, // XK_Meta_L? + { kVK_RightCommand, XK_Super_R }, // XK_Meta_R? + { kVK_Control, XK_Control_L }, + { kVK_RightControl, XK_Control_R }, + { kVK_Function, XK_Hyper_L }, + { kVK_Option, XK_Alt_L }, + { kVK_RightOption, XK_Alt_R }, + { kVK_Shift, XK_Shift_L }, + { kVK_RightShift, XK_Shift_R }, // special - { OSX_VK_DELETE, XK_BackSpace }, - { OSX_VK_ENTER, XK_KP_Enter }, + { kVK_Delete, XK_BackSpace }, //OSX_VK_ENTER_POWERBOOK -> ? - { OSX_VK_ESCAPE, XK_Escape }, - { OSX_VK_FORWARD_DELETE, XK_Delete }, - //{OSX_VK_HELP, XK_Help}, // the same keycode as OSX_VK_PC_INSERT - { OSX_VK_RETURN, XK_Return }, - { OSX_VK_SPACE, XK_space }, - { OSX_VK_TAB, XK_Tab }, + { kVK_Escape, XK_Escape }, + { kVK_ForwardDelete, XK_Delete }, + //{kVK_Help, XK_Help}, // the same keycode as kVK_PC_INSERT + { kVK_Return, XK_Return }, + { kVK_Space, XK_space }, + { kVK_Tab, XK_Tab }, // function - { OSX_VK_F1, XK_F1 }, - { OSX_VK_F2, XK_F2 }, - { OSX_VK_F3, XK_F3 }, - { OSX_VK_F4, XK_F4 }, - { OSX_VK_F5, XK_F5 }, - { OSX_VK_F6, XK_F6 }, - { OSX_VK_F7, XK_F7 }, - { OSX_VK_F8, XK_F8 }, - { OSX_VK_F9, XK_F9 }, - { OSX_VK_F10, XK_F10 }, - { OSX_VK_F11, XK_F11 }, - { OSX_VK_F12, XK_F12 }, - { OSX_VK_F13, XK_F13 }, - { OSX_VK_F14, XK_F14 }, - { OSX_VK_F15, XK_F15 }, - { OSX_VK_F16, XK_F16 }, - { OSX_VK_F17, XK_F17 }, - { OSX_VK_F18, XK_F18 }, - { OSX_VK_F19, XK_F19 }, + { kVK_F1, XK_F1 }, + { kVK_F2, XK_F2 }, + { kVK_F3, XK_F3 }, + { kVK_F4, XK_F4 }, + { kVK_F5, XK_F5 }, + { kVK_F6, XK_F6 }, + { kVK_F7, XK_F7 }, + { kVK_F8, XK_F8 }, + { kVK_F9, XK_F9 }, + { kVK_F10, XK_F10 }, + { kVK_F11, XK_F11 }, + { kVK_F12, XK_F12 }, + { kVK_F13, XK_F13 }, + { kVK_F14, XK_F14 }, + { kVK_F15, XK_F15 }, + { kVK_F16, XK_F16 }, + { kVK_F17, XK_F17 }, + { kVK_F18, XK_F18 }, + { kVK_F19, XK_F19 }, + { kVK_F20, XK_F20 }, // cursor - { OSX_VK_CURSOR_UP, XK_Up }, - { OSX_VK_CURSOR_DOWN, XK_Down }, - { OSX_VK_CURSOR_LEFT, XK_Left }, - { OSX_VK_CURSOR_RIGHT, XK_Right }, - { OSX_VK_PAGEUP, XK_Page_Up }, - { OSX_VK_PAGEDOWN, XK_Page_Down }, - { OSX_VK_HOME, XK_Home }, - { OSX_VK_END, XK_End }, + { kVK_UpArrow, XK_Up }, + { kVK_DownArrow, XK_Down }, + { kVK_LeftArrow, XK_Left }, + { kVK_RightArrow, XK_Right }, + { kVK_PageUp, XK_Page_Up }, + { kVK_PageDown, XK_Page_Down }, + { kVK_Home, XK_Home }, + { kVK_End, XK_End }, // keypad - { OSX_VK_KEYPAD_0, XK_KP_0 }, - { OSX_VK_KEYPAD_1, XK_KP_1 }, - { OSX_VK_KEYPAD_2, XK_KP_2 }, - { OSX_VK_KEYPAD_3, XK_KP_3 }, - { OSX_VK_KEYPAD_4, XK_KP_4 }, - { OSX_VK_KEYPAD_5, XK_KP_5 }, - { OSX_VK_KEYPAD_6, XK_KP_6 }, - { OSX_VK_KEYPAD_7, XK_KP_7 }, - { OSX_VK_KEYPAD_8, XK_KP_8 }, - { OSX_VK_KEYPAD_9, XK_KP_9 }, - { OSX_VK_KEYPAD_CLEAR, XK_Clear }, - { OSX_VK_KEYPAD_COMMA, XK_KP_Separator }, - { OSX_VK_KEYPAD_DOT, XK_KP_Decimal }, - { OSX_VK_KEYPAD_EQUAL, XK_KP_Equal }, - { OSX_VK_KEYPAD_MINUS, XK_KP_Subtract }, - { OSX_VK_KEYPAD_MULTIPLY, XK_KP_Multiply }, - { OSX_VK_KEYPAD_PLUS, XK_KP_Add }, - { OSX_VK_KEYPAD_SLASH, XK_KP_Divide }, + { kVK_ANSI_Keypad0, XK_KP_0 }, + { kVK_ANSI_Keypad1, XK_KP_1 }, + { kVK_ANSI_Keypad2, XK_KP_2 }, + { kVK_ANSI_Keypad3, XK_KP_3 }, + { kVK_ANSI_Keypad4, XK_KP_4 }, + { kVK_ANSI_Keypad5, XK_KP_5 }, + { kVK_ANSI_Keypad6, XK_KP_6 }, + { kVK_ANSI_Keypad7, XK_KP_7 }, + { kVK_ANSI_Keypad8, XK_KP_8 }, + { kVK_ANSI_Keypad9, XK_KP_9 }, + { kVK_ANSI_KeypadEnter, XK_KP_Enter }, + { kVK_ANSI_KeypadClear, XK_Clear }, + { kVK_ANSI_KeypadDecimal, XK_KP_Decimal }, + { kVK_ANSI_KeypadEquals, XK_KP_Equal }, + { kVK_ANSI_KeypadMinus, XK_KP_Subtract }, + { kVK_ANSI_KeypadMultiply, XK_KP_Multiply }, + { kVK_ANSI_KeypadPlus, XK_KP_Add }, + { kVK_ANSI_KeypadDivide, XK_KP_Divide }, // pc keyboard - { OSX_VK_PC_APPLICATION, XK_Menu }, - { OSX_VK_PC_INSERT, XK_Insert }, - //{OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock}, // the same keycode as OSX_VK_KEYPAD_CLEAR - { OSX_VK_PC_PAUSE, XK_Pause }, - //OSX_VK_PC_POWER -> ? - { OSX_VK_PC_PRINTSCREEN, XK_Print }, - { OSX_VK_PC_SCROLLLOCK, XK_Scroll_Lock }, + { kVK_PC_Application, XK_Menu }, + { kVK_PC_Insert, XK_Insert }, + //{kVK_PC_Keypad NumLock, XK_Num_Lock}, // the same keycode as kVK_ANSI_KeypadClear + { kVK_PC_Pause, XK_Pause }, + //OSX_VK_PC_Power -> ? + { kVK_PC_PrintScreen, XK_Print }, + { kVK_PC_ScrollLock, XK_Scroll_Lock }, // JIS keyboard - { OSX_VK_JIS_EISUU, XK_Eisu_toggle }, - { OSX_VK_JIS_KANA, XK_Kana_Lock }, + { kVK_JIS_KeypadComma, XK_KP_Separator }, + { kVK_JIS_Eisu, XK_Eisu_toggle }, + { kVK_JIS_Kana, XK_Kana_Shift }, - { -1, -1 } + { -1, -1 } }; -int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) { - for (struct keycode_mapping_t *mapping = keycode_mappings; - mapping->osx_keycode >= 0; +static struct mapping_t keychar_mappings[] = { + // ASCII control characters + { NSEnterCharacter, XK_KP_Enter }, + { NSBackspaceCharacter, XK_BackSpace }, + { NSTabCharacter, XK_Tab }, + { NSNewlineCharacter, XK_Linefeed }, + { NSCarriageReturnCharacter, XK_Return }, + { NSBackTabCharacter, XK_ISO_Left_Tab }, + { NSDeleteCharacter, XK_Delete }, + // Nagivator key characters + { NSUpArrowFunctionKey, XK_Up }, + { NSDownArrowFunctionKey, XK_Down }, + { NSLeftArrowFunctionKey, XK_Left }, + { NSRightArrowFunctionKey, XK_Right }, + // Function key characters + { NSF1FunctionKey, XK_F1 }, + { NSF2FunctionKey, XK_F2 }, + { NSF3FunctionKey, XK_F3 }, + { NSF4FunctionKey, XK_F4 }, + { NSF5FunctionKey, XK_F5 }, + { NSF6FunctionKey, XK_F6 }, + { NSF7FunctionKey, XK_F7 }, + { NSF8FunctionKey, XK_F8 }, + { NSF9FunctionKey, XK_F9 }, + { NSF10FunctionKey, XK_F10 }, + { NSF11FunctionKey, XK_F11 }, + { NSF12FunctionKey, XK_F12 }, + { NSF13FunctionKey, XK_F13 }, + { NSF14FunctionKey, XK_F14 }, + { NSF15FunctionKey, XK_F15 }, + { NSF16FunctionKey, XK_F16 }, + { NSF17FunctionKey, XK_F17 }, + { NSF18FunctionKey, XK_F18 }, + { NSF19FunctionKey, XK_F19 }, + { NSF20FunctionKey, XK_F20 }, + { NSF21FunctionKey, XK_F21 }, + { NSF22FunctionKey, XK_F22 }, + { NSF23FunctionKey, XK_F23 }, + { NSF24FunctionKey, XK_F24 }, + { NSF25FunctionKey, XK_F25 }, + { NSF26FunctionKey, XK_F26 }, + { NSF27FunctionKey, XK_F27 }, + { NSF28FunctionKey, XK_F28 }, + { NSF29FunctionKey, XK_F29 }, + { NSF30FunctionKey, XK_F30 }, + { NSF31FunctionKey, XK_F31 }, + { NSF32FunctionKey, XK_F32 }, + { NSF33FunctionKey, XK_F33 }, + { NSF34FunctionKey, XK_F34 }, + { NSF35FunctionKey, XK_F35 }, + // Misc functional key characters + { NSInsertFunctionKey, XK_Insert }, + { NSDeleteFunctionKey, XK_Delete }, + { NSHomeFunctionKey, XK_Home }, + { NSBeginFunctionKey, XK_Begin }, + { NSEndFunctionKey, XK_End }, + { NSPageUpFunctionKey, XK_Page_Up }, + { NSPageDownFunctionKey, XK_Page_Down }, + { NSScrollLockFunctionKey, XK_Scroll_Lock }, + { NSPauseFunctionKey, XK_Pause }, + { NSSysReqFunctionKey, XK_Sys_Req }, + { NSBreakFunctionKey, XK_Break }, + { NSStopFunctionKey, XK_Cancel }, + { NSMenuFunctionKey, XK_Menu }, + { NSPrintFunctionKey, XK_Print }, + { NSClearLineFunctionKey, XK_Clear }, + { NSClearDisplayFunctionKey, XK_Num_Lock }, + { NSSelectFunctionKey, XK_Select }, + { NSExecuteFunctionKey, XK_Execute }, + { NSUndoFunctionKey, XK_Undo }, + { NSRedoFunctionKey, XK_Redo }, + { NSFindFunctionKey, XK_Find }, + { NSHelpFunctionKey, XK_Help }, + { NSModeSwitchFunctionKey, XK_Mode_switch }, + + { -1, -1 } +}; + +int get_rime_keycode(ushort keycode, unichar keychar, bool shift, bool caps) { + for (struct mapping_t *mapping = keycode_mappings; + mapping->from_osx >= 0; ++mapping) { - if (keycode == mapping->osx_keycode) { - return mapping->rime_keycode; + if (keycode == mapping->from_osx) { + return mapping->to_rime; } } // NOTE: IBus/Rime use different keycodes for uppercase/lowercase letters. - if (keychar >= 'a' && keychar <= 'z' && (!!shift != !!caps)) { + if (keychar >= 'a' && keychar <= 'z' && (shift != caps)) { // lowercase -> Uppercase return keychar - 'a' + 'A'; } @@ -133,211 +215,13 @@ int osx_keycode_to_rime_keycode(int keycode, int keychar, int shift, int caps) { return keychar; } - switch (keychar) { - case 0x0003: - return XK_KP_Enter; - break; - case 0x0008: - return XK_BackSpace; - break; - case 0x0009: - case 0x0019: - return XK_Tab; - break; - case 0x000a: - return XK_Return; - break; - case 0xF728: - return XK_Delete; - break; - case 0xF700: - return XK_Up; - break; - case 0xF701: - return XK_Down; - break; - case 0xF702: - return XK_Left; - break; - case 0xF703: - return XK_Right; - break; - case 0xF704: - return XK_F1; - break; - case 0xF705: - return XK_F2; - break; - case 0xF706: - return XK_F3; - break; - case 0xF707: - return XK_F4; - break; - case 0xF708: - return XK_F5; - break; - case 0xF709: - return XK_F6; - break; - case 0xF70A: - return XK_F7; - break; - case 0xF70B: - return XK_F8; - break; - case 0xF70C: - return XK_F9; - break; - case 0xF70D: - return XK_F10; - break; - case 0xF70E: - return XK_F11; - break; - case 0xF70F: - return XK_F12; - break; - case 0xF710: - return XK_F13; - break; - case 0xF711: - return XK_F14; - break; - case 0xF712: - return XK_F15; - break; - case 0xF713: - return XK_F16; - break; - case 0xF714: - return XK_F17; - break; - case 0xF715: - return XK_F18; - break; - case 0xF716: - return XK_F19; - break; - case 0xF717: - return XK_F20; - break; - case 0xF718: - return XK_F21; - break; - case 0xF719: - return XK_F22; - break; - case 0xF71A: - return XK_F23; - break; - case 0xF71B: - return XK_F24; - break; - case 0xF71C: - return XK_F25; - break; - case 0xF71D: - return XK_F26; - break; - case 0xF71E: - return XK_F27; - break; - case 0xF71F: - return XK_F28; - break; - case 0xF720: - return XK_F29; - break; - case 0xF721: - return XK_F30; - break; - case 0xF722: - return XK_F31; - break; - case 0xF723: - return XK_F32; - break; - case 0xF724: - return XK_F33; - break; - case 0xF725: - return XK_F34; - break; - case 0xF726: - return XK_F35; - break; - case 0xF72A: - return XK_Begin; - break; - case 0xF72C: - return XK_Page_Up; - break; - case 0xF72D: - return XK_Page_Down; - break; - case 0xF729: - return XK_Home; - break; - case 0xF72B: - return XK_End; - break; - case 0xF732: - return XK_Break; - break; - case 0xF73A: - return XK_Clear; - break; - case 0xF739: - return XK_Num_Lock; - break; - case 0xF73E: - return XK_Delete; - break; - case 0xF742: - return XK_Execute; - break; - case 0xF745: - return XK_Find; - break; - case 0xF746: - return XK_Help; - break; - case 0xF727: - return XK_Insert; - break; - case 0xF735: - return XK_Menu; - break; - case 0xF747: - return XK_Mode_switch; - break; - case 0xF730: - return XK_Pause; - break; - case 0xF738: - return XK_Print; - break; - case 0xF744: - return XK_Redo; - break; - case 0xF72F: - return XK_Scroll_Lock; - break; - case 0xF741: - return XK_Select; - break; - case 0xF734: - return XK_Cancel; - break; - case 0xF731: - return XK_Sys_Req; - break; - case 0xF743: - return XK_Undo; - break; - default: - return XK_VoidSymbol; - break; + for (struct mapping_t *mapping = keychar_mappings; + mapping->from_osx >= 0; + ++mapping) { + if (keycode == mapping->from_osx) { + return mapping->to_rime; + } } + + return XK_VoidSymbol; } diff --git a/main.m b/main.m index ed952c24d..40675c1a2 100644 --- a/main.m +++ b/main.m @@ -2,14 +2,13 @@ #import #import #import -#import -typedef enum RimeInputMode : int { +typedef NS_OPTIONS(int, RimeInputMode) { DEFAULT_INPUT_MODE = 1 << 0, HANS_INPUT_MODE = 1 << 0, HANT_INPUT_MODE = 1 << 1, CANT_INPUT_MODE = 1 << 2 -} RimeInputMode; +}; void RegisterInputSource(void); RimeInputMode GetEnabledInputModes(void); @@ -18,7 +17,7 @@ // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. -static NSString *kConnectionName = @"Squirrel_1_Connection"; +static NSString *const kConnectionName = @"Squirrel_1_Connection"; int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--quit", argv[1])) { @@ -49,7 +48,7 @@ int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--build", argv[1])) { // notification - show_message("deploy_update", "deploy"); + show_notification("deploy_update"); // build all schemas in current directory RIME_STRUCT(RimeTraits, builder_traits); builder_traits.app_name = "rime.squirrel-builder"; From a951bda65e9c2292ebab73690fa6df49c10df0ef Mon Sep 17 00:00:00 2001 From: groverlynn Date: Sat, 10 Feb 2024 10:15:58 +0100 Subject: [PATCH 160/164] Update librime --- Info.plist | 8 ++++++-- SquirrelInputController.m | 2 +- librime | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Info.plist b/Info.plist index ad3a697e0..1a4e2172c 100644 --- a/Info.plist +++ b/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - English + en CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile @@ -20,6 +20,10 @@ ${MARKETING_VERSION} CFBundleSignature ???? + CFBundleSupportedPlatforms + + MacOSX + CFBundleVersion $(CURRENT_PROJECT_VERSION) ComponentInputModeDict @@ -126,7 +130,7 @@ LSHasLocalizedDisplayName LSUIElement - + 1 NSMainNibFile MainMenu NSPrincipalClass diff --git a/SquirrelInputController.m b/SquirrelInputController.m index c8c85a521..ebd7c47eb 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -244,7 +244,7 @@ - (void)perform:(rimeAction)action handled = rime_get_api()->select_candidate_on_current_page(_session, (size_t)index); break; case kHILITE: - handled = rime_get_api()->hilite_candidate_on_current_page(_session, (size_t)index); + handled = rime_get_api()->highlight_candidate_on_current_page(_session, (size_t)index); break; } } diff --git a/librime b/librime index af9fef8ca..7bc943ddf 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit af9fef8ca4ebf616c0413a8f57b8de25cfdad062 +Subproject commit 7bc943ddf06d9674da6dfffd5695db3dfec0a8d5 From 875293928edfd4840938bea9d8ffa4a017ef3e3e Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 14 Feb 2024 04:54:49 +0100 Subject: [PATCH 161/164] update --- SquirrelApplicationDelegate.m | 38 ++-- SquirrelConfig.h | 5 +- SquirrelConfig.m | 12 +- SquirrelInputController.h | 16 +- SquirrelInputController.m | 209 +++++++++++++---- SquirrelPanel.h | 8 +- SquirrelPanel.m | 367 ++++++++++++++++++------------ en.lproj/Localizable.strings | 8 + zh-HK.lproj/Localizable.strings | 8 + zh-Hans.lproj/Localizable.strings | 8 + zh-Hant.lproj/Localizable.strings | 8 + 11 files changed, 464 insertions(+), 223 deletions(-) diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 721491f77..f28146d8e 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -69,8 +69,8 @@ void show_notification(const char *msg_text) { }]; } else { NSUserNotification *notification = [[NSUserNotification alloc] init]; - [notification setTitle:NSLocalizedString(@"Squirrel", nil)]; - [notification setSubtitle:NSLocalizedString(@(msg_text), nil)]; + notification.title = NSLocalizedString(@"Squirrel", nil); + notification.subtitle = NSLocalizedString(@(msg_text), nil); NSUserNotificationCenter *notificationCenter = NSUserNotificationCenter.defaultUserNotificationCenter; @@ -82,8 +82,7 @@ void show_notification(const char *msg_text) { static void show_status(const char *msg_text_long, const char *msg_text_short) { NSString *msgLong = msg_text_long ? @(msg_text_long) : nil; NSString *msgShort = msg_text_short ? @(msg_text_short) : - (msgLong ? [msgLong substringWithRange: - [msgLong rangeOfComposedCharacterSequenceAtIndex:0]] : nil); + [msgLong substringWithRange:[msgLong rangeOfComposedCharacterSequenceAtIndex:0]]; [NSApp.squirrelAppDelegate.panel updateStatusLong:msgLong statusShort:msgShort]; } @@ -174,7 +173,7 @@ - (void)shutdownRime { rime_get_api()->finalize(); } -NSArray * getScriptOptionsForSchema(SquirrelConfig *schema) { +NSArray *getScriptOptionsForSchema(SquirrelConfig *schema) { NSUInteger numSwitches = [schema getListSize:@"switches"]; if (numSwitches == 0) { return nil; @@ -199,8 +198,8 @@ - (void)shutdownRime { return nil; } -SquirrelOptionSwitcher * updateOptionSwitcher(SquirrelOptionSwitcher *optionSwitcher, - RimeSessionId sessionId) { +SquirrelOptionSwitcher *updateOptionSwitcher(SquirrelOptionSwitcher *optionSwitcher, + RimeSessionId sessionId) { NSMutableDictionary *switcher = optionSwitcher.mutableSwitcher; NSSet *prevStates = [NSSet setWithArray:optionSwitcher.optionStates]; for (NSString *state in prevStates) { @@ -247,22 +246,17 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId return; } // update the list of switchers that change styles and color-themes - SquirrelOptionSwitcher *optionSwitcher; SquirrelConfig *schema = [[SquirrelConfig alloc] init]; - if ([schema openWithSchemaId:schemaId baseConfig:self.config]) { - self.panel.scriptOptions = getScriptOptionsForSchema(schema); - if ([schema hasSection:@"style"]) { - optionSwitcher = [schema getOptionSwitcher]; - self.panel.optionSwitcher = updateOptionSwitcher(optionSwitcher, sessionId); - [self.panel loadConfig:schema forAppearance:defaultAppear]; - [self.panel loadConfig:schema forAppearance:darkAppear]; - } else { - self.panel.optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:schemaId - switcher:@{} - optionGroups:@{}]; - [self.panel loadConfig:self.config forAppearance:defaultAppear]; - [self.panel loadConfig:self.config forAppearance:darkAppear]; - } + if ([schema openWithSchemaId:schemaId baseConfig:self.config] && + [schema hasSection:@"style"]) { + SquirrelOptionSwitcher *optionSwitcher = [schema getOptionSwitcher]; + self.panel.optionSwitcher = updateOptionSwitcher(optionSwitcher, sessionId); + [self.panel loadConfig:schema forAppearance:defaultAppear]; + [self.panel loadConfig:schema forAppearance:darkAppear]; + } else { + self.panel.optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:schemaId]; + [self.panel loadConfig:self.config forAppearance:defaultAppear]; + [self.panel loadConfig:self.config forAppearance:darkAppear]; } [schema close]; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 97e273907..4329c4b58 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -15,10 +15,13 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; switcher:(NSDictionary *)switcher optionGroups:(NSDictionary *> *)optionGroups; +- (instancetype)initWithSchemaId:(NSString *)schemaId; + // return whether switcher options has been successfully updated - (BOOL)updateSwitcher:(NSDictionary *)switcher; -- (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName; +- (BOOL)updateGroupState:(NSString *)optionState + ofOption:(NSString *)optionName; - (BOOL)containsOption:(NSString *)optionName; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 1c7c30639..016880659 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -16,6 +16,16 @@ - (instancetype)initWithSchemaId:(NSString *)schemaId return self; } +- (instancetype)initWithSchemaId:(NSString *)schemaId { + if (self = [super init]) { + _schemaId = schemaId; + _switcher = nil; + _optionGroups = nil; + _optionNames = nil; + } + return self; +} + - (NSArray *)optionStates { return _switcher.allValues; } @@ -39,7 +49,7 @@ - (BOOL)updateSwitcher:(NSDictionary *)switcher { - (BOOL)updateGroupState:(NSString *)optionState ofOption:(NSString *)optionName { NSArray *optionGroup = _optionGroups[optionName]; - if (!optionGroup || ![optionGroup containsObject:optionState]) { + if (![optionGroup containsObject:optionState]) { return NO; } NSMutableDictionary *updatedSwitcher = [_switcher mutableCopy]; diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 8cd1dbc17..9ae167778 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -5,14 +5,15 @@ typedef enum { kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape) kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A') kDELETE = 3 // only accepts indices in digits (int 1) -} rimeAction; +} SquirrelAction; -typedef NS_ENUM(NSUInteger, rimeIndex) { +typedef NS_ENUM(NSUInteger, SquirrelIndex) { // 0 ... 9 are ordinal digits, used as (int) index // 0x21 ... 0x7e are ASCII chars (as selection keys) // other rime keycodes (as function keys), for paging etc. kBackSpace = 0xff08, // XK_BackSpace kEscape = 0xff1b, // XK_Escape + kCodeInput = 0xff37, // XK_Codeinput kHome = 0xff50, // XK_Home kPageUp = 0xff55, // XK_Page_Up kPageDown = 0xff56, // XK_Page_Down @@ -22,7 +23,14 @@ typedef NS_ENUM(NSUInteger, rimeIndex) { @interface SquirrelInputController : IMKInputController -- (void)perform:(rimeAction)action - onIndex:(rimeIndex)index; +@property(nonatomic, class, readonly) SquirrelInputController *currentController; + +- (void)moveCursor:(NSUInteger)cursorPosition + toPosition:(NSUInteger)targetPosition + inlinePreedit:(BOOL)inlinePreedit + inlineCandidate:(BOOL)inlineCandidate; + +- (void)perform:(SquirrelAction)action + onIndex:(SquirrelIndex)index; @end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index ebd7c47eb..6f50f0405 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -39,6 +39,29 @@ @implementation SquirrelInputController { NSTimeInterval _chordDuration; } +static SquirrelInputController *_currentController = nil; +static NSMapTable *_controllerDeactivationTime = NSMapTable.weakToWeakObjectsMapTable; + ++ (void)setCurrentController:(SquirrelInputController *)controller { + _currentController = controller; +} + ++ (SquirrelInputController *)currentController { + return _currentController; +} + ++ (void)setDeactivationTimeForController:(SquirrelInputController *)controller { + [_controllerDeactivationTime setObject:NSDate.now forKey:controller]; +} + ++ (NSDate *)lastControllerDeactivationTime { + return [_controllerDeactivationTime objectForKey:_currentController]; +} + ++ (void)removeDeactivationTimeForController:(SquirrelInputController *)controller { + [_controllerDeactivationTime removeObjectForKey:controller]; +} + /*! @method @abstract Receive incoming event @@ -166,6 +189,39 @@ - (BOOL)handleEvent:(NSEvent *)event return handled; } +- (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index + coordinate:(NSPoint)point + withModifier:(NSUInteger)flags + continueTracking:(BOOL *)keepTracking + client:(id)sender { + *keepTracking = NO; + @autoreleasepool { + if ((!_inlinePreedit && !_inlineCandidate) || _caretPos == index || + (flags & NSEventModifierFlagDeviceIndependentFlagsMask)) { + return NO; + } + NSRange markedRange = [sender markedRange]; + NSPoint head = [[sender attributesForCharacterIndex:0 + lineHeightRectangle:NULL][@"IMKBaseline"] pointValue]; + NSPoint tail = [[sender attributesForCharacterIndex:markedRange.length - 1 + lineHeightRectangle:NULL][@"IMKBaseline"] pointValue]; + if (point.x > tail.x || index >= markedRange.length -1) { + if (_inlineCandidate && !_inlinePreedit) { + return NO; + } + [self perform:kSELECT onIndex:kEnd]; + } else if (point.x < head.x || index <= 0) { + [self perform:kSELECT onIndex:kHome]; + } else { + [self moveCursor:_caretPos + toPosition:index + inlinePreedit:_inlinePreedit + inlineCandidate:_inlineCandidate]; + } + return YES; + } +} + void set_CapsLock_LED_state(bool target_state) { io_service_t ioService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass)); @@ -226,9 +282,48 @@ - (BOOL)processKey:(int)rime_keycode return handled; } +- (void)moveCursor:(NSUInteger)cursorPosition + toPosition:(NSUInteger)targetPosition + inlinePreedit:(BOOL)inlinePreedit + inlineCandidate:(BOOL)inlineCandidate { + BOOL vertical = NSApp.squirrelAppDelegate.panel.vertical; + NSString *composition = !inlinePreedit && !inlineCandidate ? _composedString : _preeditString.string; + RIME_STRUCT(RimeContext, ctx); + if (cursorPosition > targetPosition) { + NSString *targetPrefix = [composition substringToIndex:targetPosition]; + NSString *prefix = [composition substringToIndex:cursorPosition]; + while (targetPrefix.length < prefix.length) { + rime_get_api()->process_key(_session, vertical ? XK_Up : XK_Left, kControlMask); + rime_get_api()->get_context(_session, &ctx); + if (inlineCandidate) { + size_t length = ctx.composition.cursor_pos < ctx.composition.sel_end ? + (size_t)ctx.composition.cursor_pos : strlen(ctx.commit_text_preview) - + (inlinePreedit ? 0 : (size_t)(ctx.composition.cursor_pos - ctx.composition.sel_end)); + prefix = [[NSString alloc] initWithBytes:ctx.commit_text_preview + length:(NSUInteger)length + encoding:NSUTF8StringEncoding]; + } else { + prefix = [[NSString alloc] initWithBytes:ctx.composition.preedit + length:(NSUInteger)ctx.composition.cursor_pos + encoding:NSUTF8StringEncoding]; + } + rime_get_api()->free_context(&ctx); + } + } else if (cursorPosition < targetPosition) { + NSString *targetSuffix = [composition substringFromIndex:targetPosition]; + NSString *suffix = [composition substringFromIndex:cursorPosition]; + while (targetSuffix.length < suffix.length) { + rime_get_api()->process_key(_session, vertical ? XK_Down : XK_Right, kControlMask); + rime_get_api()->get_context(_session, &ctx); + suffix = @(ctx.composition.preedit + ctx.composition.cursor_pos + (!inlinePreedit && !inlineCandidate ? 3 : 0)); + rime_get_api()->free_context(&ctx); + } + } + [self rimeUpdate]; +} -- (void)perform:(rimeAction)action - onIndex:(rimeIndex)index { +- (void)perform:(SquirrelAction)action + onIndex:(SquirrelIndex)index { //NSLog(@"perform action: %u on index: %lu", action, (unsigned long)index); bool handled = false; if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) { @@ -313,7 +408,20 @@ - (void)clearChord { - (NSUInteger)recognizedEvents:(id)sender { //NSLog(@"recognizedEvents:"); - return NSEventMaskKeyDown | NSEventMaskFlagsChanged; + return NSEventMaskKeyDown | NSEventMaskFlagsChanged | NSEventMaskLeftMouseDown; +} + +NSString *getOptionLabel(RimeSessionId session, const char *option, Bool state) { + RimeStringSlice short_label = rime_get_api()-> + get_state_label_abbreviated(session, option, state, True); + if (short_label.str && short_label.length >= strlen(short_label.str)) { + return @(short_label.str); + } else { + RimeStringSlice long_label = rime_get_api()-> + get_state_label_abbreviated(session, option, state, False); + NSString *label = long_label.str ? @(long_label.str) : nil; + return [label substringWithRange:[label rangeOfComposedCharacterSequenceAtIndex:0]]; + } } - (void)showInitialStatus { @@ -321,15 +429,30 @@ - (void)showInitialStatus { if (_session && rime_get_api()->get_status(_session, &status)) { _schemaId = @(status.schema_id); NSString *schemaName = status.schema_name ? @(status.schema_name) : @(status.schema_id); + NSMutableArray *options = [[NSMutableArray alloc] initWithCapacity:3]; + NSString *asciiMode = getOptionLabel(_session, "ascii_mode", status.is_ascii_mode); + if (asciiMode) { + [options addObject:asciiMode]; + } + NSString *fullShape = getOptionLabel(_session, "full_shape", status.is_full_shape); + if (fullShape) { + [options addObject:fullShape]; + } + NSString *asciiPunct = getOptionLabel(_session, "ascii_punct", status.is_ascii_punct); + if (asciiPunct) { + [options addObject:asciiPunct]; + } rime_get_api()->free_status(&status); - [NSApp.squirrelAppDelegate.panel updateStatusLong:schemaName statusShort:@""]; + NSString *foldedOptions = options.count == 0 ? schemaName : + [NSString stringWithFormat:@"%@|%@", schemaName, [options componentsJoinedByString:@" "]]; + [NSApp.squirrelAppDelegate.panel updateStatusLong:foldedOptions statusShort:schemaName]; [self rimeUpdate]; } } - (void)activateServer:(id)sender { //NSLog(@"activateServer:"); - NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config + NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config getString:@"keyboard_layout"]; if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { @@ -344,6 +467,15 @@ - (void)activateServer:(id)sender { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } + if (NSApp.squirrelAppDelegate.showNotifications == kShowNotificationsAlways) { + if (!SquirrelInputController.currentController || + (![SquirrelInputController.currentController isEqualTo:self] && + [SquirrelInputController.lastControllerDeactivationTime timeIntervalSinceNow] < -0.5)) { + [self showInitialStatus]; + } + } + [SquirrelInputController setCurrentController:self]; + SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; if ([defaultConfig openWithConfigId:@"default"] && [defaultConfig hasSection:@"ascii_composer"]) { @@ -365,15 +497,13 @@ - (instancetype)initWithServer:(IMKServer *)server _originalString = [[NSMutableString alloc] init]; _composedString = [[NSMutableString alloc] init]; } - if (NSApp.squirrelAppDelegate.showNotifications == kShowNotificationsAlways) { - [self showInitialStatus]; - } return self; } - (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); [self commitComposition:sender]; + [SquirrelInputController setDeactivationTimeForController:self]; [super deactivateServer:sender]; } @@ -401,9 +531,14 @@ - (void)inputControllerWillClose { if (_session) { [self destroySession]; } + if ([SquirrelInputController.currentController isEqualTo:self]) { + [SquirrelInputController setCurrentController:nil]; + } + [SquirrelInputController removeDeactivationTimeForController:self]; _preeditString = nil; _originalString = nil; _composedString = nil; + [super inputControllerWillClose]; } // a piece of comment from SunPinyin's macos wrapper says: @@ -444,7 +579,8 @@ - (NSAttributedString *)originalString:(id)sender { } - (id)composedString:(id)sender { - return _composedString; + return [_composedString stringByReplacingOccurrencesOfString:@" " + withString:@""]; } - (NSArray *)candidates:(id)sender { @@ -512,7 +648,7 @@ - (void)showPreeditString:(NSString *)preedit _caretPos = pos; //NSLog(@"selRange.location = %ld, selRange.length = %ld; caretPos = %ld", // range.location, range.length, pos); - NSDictionary* attrs = [self markForStyle:kTSMHiliteSelectedRawText + NSDictionary* attrs = [self markForStyle:kTSMHiliteRawText atRange:NSMakeRange(0, preedit.length)]; _preeditString = [[NSMutableAttributedString alloc] initWithString:preedit attributes:attrs]; @@ -522,7 +658,7 @@ - (void)showPreeditString:(NSString *)preedit range:NSMakeRange(0, range.location)]; } if (range.location < pos) { - [_preeditString addAttributes:[self markForStyle:kTSMHiliteSelectedConvertedText + [_preeditString addAttributes:[self markForStyle:kTSMHiliteSelectedRawText atRange:range] range:range]; } @@ -586,7 +722,6 @@ - (void)showPanelWithPreedit:(NSString *)preedit } } } - panel.inputController = self; panel.IbeamRect = IbeamRect; [panel showPreedit:preedit selRange:selRange @@ -655,6 +790,12 @@ - (BOOL)rimeConsumeCommittedText { return NO; } +NSUInteger UTF8LengthToUTF16Length(const char* cstring, int length) { + return [[NSString alloc] initWithBytes:cstring + length:(NSUInteger)length + encoding:NSUTF8StringEncoding].length; +} + - (void)rimeUpdate { //NSLog(@"rimeUpdate"); BOOL didCommit = [self rimeConsumeCommittedText]; @@ -699,28 +840,20 @@ - (void)rimeUpdate { if (!preedit || _showingSwitcherMenu) { _composedString = @""; } else if (rime_get_api()->get_option(_session, "soft_cursor")) { - size_t cursorPos = (size_t)ctx.composition.cursor_pos - - (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0); + size_t cursorPos = (size_t)ctx.composition.cursor_pos - + (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0); char composed[strlen(preedit) - 2]; strlcpy(composed, preedit, cursorPos + 1); strlcat(composed, preedit + cursorPos + 3, strlen(preedit) - 2); - _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; + _composedString = @(composed); } else { - _composedString = [@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]; + _composedString = @(preedit); } - NSUInteger start = [[NSString alloc] initWithBytes:preedit - length:(NSUInteger)ctx.composition.sel_start - encoding:NSUTF8StringEncoding].length; - NSUInteger end = [[NSString alloc] initWithBytes:preedit - length:(NSUInteger)ctx.composition.sel_end - encoding:NSUTF8StringEncoding].length; - NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit - length:(NSUInteger)ctx.composition.cursor_pos - encoding:NSUTF8StringEncoding].length; - NSUInteger length = [[NSString alloc] initWithBytes:preedit - length:(NSUInteger)ctx.composition.length - encoding:NSUTF8StringEncoding].length; + NSUInteger start = UTF8LengthToUTF16Length(preedit, ctx.composition.sel_start); + NSUInteger end = UTF8LengthToUTF16Length(preedit, ctx.composition.sel_end); + NSUInteger caretPos = UTF8LengthToUTF16Length(preedit, ctx.composition.cursor_pos); + NSUInteger length = UTF8LengthToUTF16Length(preedit, ctx.composition.length); NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; if (!showingStatus) { @@ -732,28 +865,26 @@ - (void)rimeUpdate { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit) { - if ((caretPos >= end) && (caretPos < length)) { + if (end <= caretPos && caretPos < length) { candidatePreviewText = [candidatePreviewText stringByAppendingString: [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; } if (!didCommit || candidatePreviewText.length > 0) { [self showPreeditString:candidatePreviewText selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) - caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (length - caretPos)]; + caretPos:caretPos < end ? caretPos : candidatePreviewText.length - (length - caretPos)]; } - } else { - if ((end < caretPos) && (caretPos > start)) { - candidatePreviewText = [candidatePreviewText substringWithRange: - NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; - } else if ((end < length) && (caretPos < end)) { - candidatePreviewText = [candidatePreviewText substringWithRange: - NSMakeRange(0, candidatePreviewText.length - (length - end))]; + } else { // preedit includes the soft cursor + if (end < caretPos && caretPos <= length) { + candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (caretPos - end)]; + } else if (caretPos < end && end < length) { + candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (length - end)]; } if (!didCommit || candidatePreviewText.length > 0) { [self showPreeditString:candidatePreviewText selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) - caretPos:caretPos < end ? caretPos : candidatePreviewText.length]; + caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; } } } else { @@ -798,4 +929,4 @@ - (void)rimeUpdate { } } -@end // SquirrelController(Private) +@end // SquirrelInputController diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 5117fd190..5c64de8a0 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -24,15 +24,11 @@ typedef enum { @property(nonatomic, readonly) BOOL inlineCandidate; // Store switch options that change style (color theme) settings @property(nonatomic, strong) SquirrelOptionSwitcher *optionSwitcher; -// option(s) on Chinese script (simplification/traditional, or radio group thereof) -@property(nonatomic, strong) NSArray *scriptOptions; +// Status message before pop-up is displayed; nil before normal panel is displayed +@property(nonatomic, readonly, strong) NSString *statusMessage; // position of the text input I-beam cursor on screen. @property(nonatomic, assign) NSRect IbeamRect; -@property(nonatomic, strong) NSString *statusMessage; - -@property(nonatomic, assign) SquirrelInputController *inputController; - - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 349fccbbc..56efb8c28 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -6,7 +6,7 @@ static const CGFloat kOffsetGap = 5; static const CGFloat kDefaultFontSize = 24; static const CGFloat kBlendedBackgroundColorFraction = 1.0 / 5; -static const NSTimeInterval kShowStatusDuration = 1.2; +static const NSTimeInterval kShowStatusDuration = 2.0; static NSString *const kDefaultCandidateFormat = @"%c. %@"; static NSString *const kTipSpecifier = @"%s"; static NSString *const kFullWidthSpace = @" "; @@ -67,8 +67,8 @@ - (CGPathRef)quartzPath { @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) -static NSString *const kMarkDownPattern = -@"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)"; +static NSString *const kMarkDownPattern = @"((\\*{1,2}|\\^|~{1,2})|((?<=\\b)_{1,2})|" + "<(b|strong|i|em|u|sup|sub|s)>)(.+?)(\\2|\\3(?=\\b)|<\\/\\4>)"; static NSString *const kRubyPattern = @"(\uFFF9\\s*)(\\S+?)(\\s*\uFFFA(.+?)\uFFFB)"; - (void)superscriptRange:(NSRange)range { @@ -612,59 +612,61 @@ - (void) setAttrs:(NSDictionary *)attrs : kFullWidthSpace) : @"\n" attributes:sepAttrs]; // Symbols for function buttons - NSTextAttachment *attmLeftFill = [[NSTextAttachment alloc] init]; - attmLeftFill.image = [NSImage imageNamed:@"Symbols/chevron.left.circle.fill"]; - NSTextAttachment *attmUpFill = [[NSTextAttachment alloc] init]; - attmUpFill.image = [NSImage imageNamed:@"Symbols/chevron.up.circle.fill"]; + NSString *attmCharacter = [NSString stringWithFormat:@"%C", (unichar)NSAttachmentCharacter]; + + NSTextAttachment *attmBackFill = [[NSTextAttachment alloc] init]; + attmBackFill.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle.fill", _linear ? @"up" : @"left"]]; NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; - attrsBackFill[NSAttachmentAttributeName] = _linear ? attmUpFill : attmLeftFill; - _symbolBackFill = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsBackFill]; - - NSTextAttachment *attmLeftStroke = [[NSTextAttachment alloc] init]; - attmLeftStroke.image = [NSImage imageNamed:@"Symbols/chevron.left.circle"]; - NSTextAttachment *attmUpStroke = [[NSTextAttachment alloc] init]; - attmUpStroke.image = [NSImage imageNamed:@"Symbols/chevron.up.circle"]; + attrsBackFill[NSAttachmentAttributeName] = attmBackFill; + attrsBackFill[NSToolTipAttributeName] = NSLocalizedString(@"page_up", nil); + _symbolBackFill = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsBackFill]; + + NSTextAttachment *attmBackStroke = [[NSTextAttachment alloc] init]; + attmBackStroke.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle", _linear ? @"up" : @"left"]]; NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; - attrsBackStroke[NSAttachmentAttributeName] = _linear ? attmUpStroke : attmLeftStroke; - _symbolBackStroke = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsBackStroke]; - - NSTextAttachment *attmRightFill = [[NSTextAttachment alloc] init]; - attmRightFill.image = [NSImage imageNamed:@"Symbols/chevron.right.circle.fill"]; - NSTextAttachment *attmDownFill = [[NSTextAttachment alloc] init]; - attmDownFill.image = [NSImage imageNamed:@"Symbols/chevron.down.circle.fill"]; + attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; + attrsBackStroke[NSToolTipAttributeName] = NSLocalizedString(@"home", nil); + _symbolBackStroke = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsBackStroke]; + + NSTextAttachment *attmForwardFill = [[NSTextAttachment alloc] init]; + attmForwardFill.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle.fill", _linear ? @"down" : @"right"]]; NSMutableDictionary *attrsForwardFill = pagingAttrs.mutableCopy; - attrsForwardFill[NSAttachmentAttributeName] = _linear ? attmDownFill : attmRightFill; - _symbolForwardFill = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsForwardFill]; - - NSTextAttachment *attmRightStroke = [[NSTextAttachment alloc] init]; - attmRightStroke.image = [NSImage imageNamed:@"Symbols/chevron.right.circle"]; - NSTextAttachment *attmDownStroke = [[NSTextAttachment alloc] init]; - attmDownStroke.image = [NSImage imageNamed:@"Symbols/chevron.down.circle"]; + attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; + attrsForwardFill[NSToolTipAttributeName] = NSLocalizedString(@"page_down", nil); + _symbolForwardFill = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsForwardFill]; + + NSTextAttachment *attmForwardStroke = [[NSTextAttachment alloc] init]; + attmForwardStroke.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle", _linear ? @"down" : @"right"]]; NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; - attrsForwardStroke[NSAttachmentAttributeName] = _linear ? attmDownStroke : attmRightStroke; - _symbolForwardStroke = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsForwardStroke]; + attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; + attrsForwardStroke[NSToolTipAttributeName] = NSLocalizedString(@"end", nil); + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsForwardStroke]; NSTextAttachment *attmDeleteFill = [[NSTextAttachment alloc] init]; attmDeleteFill.image = [NSImage imageNamed:@"Symbols/delete.backward.fill"]; NSMutableDictionary *attrsDeleteFill = preeditAttrs.mutableCopy; attrsDeleteFill[NSAttachmentAttributeName] = attmDeleteFill; + attrsDeleteFill[NSToolTipAttributeName] = NSLocalizedString(@"delete", nil); attrsDeleteFill[NSVerticalGlyphFormAttributeName] = @(NO); - attrsDeleteFill[NSKernAttributeName] = @(0.0); - _symbolDeleteFill = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsDeleteFill]; + _symbolDeleteFill = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsDeleteFill]; NSTextAttachment *attmDeleteStroke = [[NSTextAttachment alloc] init]; attmDeleteStroke.image = [NSImage imageNamed:@"Symbols/delete.backward"]; NSMutableDictionary *attrsDeleteStroke = preeditAttrs.mutableCopy; attrsDeleteStroke[NSAttachmentAttributeName] = attmDeleteStroke; + attrsDeleteStroke[NSToolTipAttributeName] = NSLocalizedString(@"escape", nil); attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @(NO); - attrsDeleteStroke[NSKernAttributeName] = @(0.0); - _symbolDeleteStroke = [[NSAttributedString alloc] - initWithString:@"\uFFFC" attributes:attrsDeleteStroke]; + _symbolDeleteStroke = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsDeleteStroke]; } - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -916,7 +918,7 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)rubyString); CGRect rubyRect = CTLineGetBoundsWithOptions((CTLineRef)CFAutorelease(line), 0); NSSize baseSize = rubyString.size; - width = MAX(0.0, rubyRect.size.width - baseSize.width); + width = fdim(rubyRect.size.width, baseSize.width); } } return NSMakeRect(glyphPosition.x, 0.0, width, glyphPosition.y); @@ -999,6 +1001,9 @@ @interface SquirrelView : NSView @property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; @property(nonatomic, readonly) CAShapeLayer *shape; @property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) NSRect preeditBlock; +@property(nonatomic, readonly) NSRect candidateBlock; +@property(nonatomic, readonly) NSRect pagingBlock; @property(nonatomic, readonly) SquirrelAppear appear; @property(nonatomic, readonly) NSEdgeInsets alignmentRectInsets; @property(nonatomic, readonly) NSArray *candidateRanges; @@ -1006,7 +1011,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSUInteger highlightedIndex; -@property(nonatomic, readonly) rimeIndex functionButton; +@property(nonatomic, readonly) SquirrelIndex functionButton; @property(nonatomic, readonly) NSBezierPath *deleteBackPath; @property(nonatomic, readonly) NSMutableArray *candidatePaths; @property(nonatomic, readonly) NSMutableArray *pagingPaths; @@ -1029,7 +1034,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets highlightedPreeditRange:(NSRange)highlightedPreeditRange pagingRange:(NSRange)pagingRange; -- (void)highlightFunctionButton:(rimeIndex)functionButton; +- (void)highlightFunctionButton:(SquirrelIndex)functionButton; - (NSUInteger)getIndexFromClickSpot:(NSPoint)spot; @@ -1050,10 +1055,9 @@ - (BOOL)wantsUpdateLayer { } - (SquirrelAppear)appear { - SquirrelPanel *panel = (SquirrelPanel *)self.window; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wundeclared-selector" - NSAppearance *effectiveAppearance = [panel.inputController.client performSelector: + NSAppearance *effectiveAppearance = [SquirrelInputController.currentController.client performSelector: @selector(viewEffectiveAppearance)] ? : NSApp.effectiveAppearance; #pragma clang diagnostic pop if ([effectiveAppearance bestMatchFromAppearancesWithNames: @@ -1366,12 +1370,13 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; _functionButton = kVoidSymbol; + [self removeAllToolTips]; // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round [self setNeedsDisplayInRect:self.bounds]; [self.textView setNeedsDisplayInRect:self.bounds]; } -- (void)highlightFunctionButton:(rimeIndex)functionButton { +- (void)highlightFunctionButton:(SquirrelIndex)functionButton { _functionButton = functionButton; if (_deleteBackPath) { [self setNeedsDisplayInRect:_deleteBackPath.bounds]; @@ -1435,7 +1440,7 @@ - (void)highlightFunctionButton:(rimeIndex)functionButton { return path; } -static inline NSArray * rectVertex(NSRect rect) { +static inline NSArray *rectVertex(NSRect rect) { return @[@(rect.origin), @(NSMakePoint(rect.origin.x, rect.origin.y + rect.size.height)), @(NSMakePoint(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height)), @@ -1443,7 +1448,7 @@ - (void)highlightFunctionButton:(rimeIndex)functionButton { } // Based on the 3 boxes from multilineRectForRange, calculate the vertex of the polygon containing the text in range -static NSArray * multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { +static NSArray *multilineRectVertex(NSRect leadingRect, NSRect bodyRect, NSRect trailingRect) { if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && NSIsEmptyRect(trailingRect)) { return rectVertex(leadingRect); } else if (NSIsEmptyRect(bodyRect) && NSIsEmptyRect(leadingRect) && !NSIsEmptyRect(trailingRect)) { @@ -1477,7 +1482,7 @@ - (void)highlightFunctionButton:(rimeIndex)functionButton { } } -static inline NSColor * hooverColor(NSColor *color, SquirrelAppear appear) { +static inline NSColor *hooverColor(NSColor *color, SquirrelAppear appear) { if (color == nil) { return nil; } @@ -1488,7 +1493,7 @@ - (void)highlightFunctionButton:(rimeIndex)functionButton { } } -static inline NSColor * disabledColor(NSColor *color, SquirrelAppear appear) { +static inline NSColor *disabledColor(NSColor *color, SquirrelAppear appear) { if (color == nil) { return nil; } @@ -1574,16 +1579,16 @@ - (void)updateLayer { NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange); // Draw preedit Rect - NSRect preeditRect = NSZeroRect; + _preeditBlock = NSZeroRect; NSBezierPath *highlightedPreeditPath; if (preeditRange.length > 0) { NSRect innerBox = [self blockRectForRange:preeditRange]; - preeditRect = NSMakeRect(backgroundRect.origin.x, - backgroundRect.origin.y, - backgroundRect.size.width, - innerBox.size.height + (candidateBlockRange.length > 0 ? theme.preeditLinespace : 0.0)); - preeditRect = [self backingAlignedRect:preeditRect - options:NSAlignAllEdgesNearest]; + _preeditBlock = NSMakeRect(backgroundRect.origin.x, + backgroundRect.origin.y, + backgroundRect.size.width, + innerBox.size.height + (candidateBlockRange.length > 0 ? theme.preeditLinespace : 0.0)); + _preeditBlock = [self backingAlignedRect:_preeditBlock + options:NSAlignAllEdgesNearest]; // Draw highlighted part of preedit text NSRange highlightedPreeditRange = NSIntersectionRange(_highlightedPreeditRange, visibleRange); @@ -1639,29 +1644,32 @@ - (void)updateLayer { deleteBackRect.size.width += floor(theme.separatorWidth * 0.5); deleteBackRect.origin.x = NSMaxX(backgroundRect) - NSWidth(deleteBackRect); deleteBackRect.origin.y += _alignmentRectInsets.top; - deleteBackRect = [self backingAlignedRect:NSIntersectionRect(deleteBackRect, preeditRect) + deleteBackRect = [self backingAlignedRect:NSIntersectionRect(deleteBackRect, _preeditBlock) options:NSAlignAllEdgesNearest]; _deleteBackPath = squirclePath(rectVertex(deleteBackRect), cornerRadius); + [self addToolTipRect:deleteBackRect owner:[_textStorage attribute:NSToolTipAttributeName + atIndex:NSMaxRange(_preeditRange) - 1 + effectiveRange:NULL] userData:nil]; } // Draw candidate Rect - NSRect candidateBlockRect = NSZeroRect; + _candidateBlock = NSZeroRect; NSBezierPath *candidateBlockPath; NSBezierPath *gridPath; if (candidateBlockRange.length > 0) { - candidateBlockRect = [self blockRectForRange:candidateBlockRange]; - candidateBlockRect.size.width = backgroundRect.size.width; - candidateBlockRect.origin.x = backgroundRect.origin.x; - candidateBlockRect.origin.y = preeditRange.length == 0 ? NSMinY(backgroundRect) : NSMaxY(preeditRect); + _candidateBlock = [self blockRectForRange:candidateBlockRange]; + _candidateBlock.size.width = backgroundRect.size.width; + _candidateBlock.origin.x = backgroundRect.origin.x; + _candidateBlock.origin.y = preeditRange.length == 0 ? NSMinY(backgroundRect) : NSMaxY(_preeditBlock); if (pagingRange.length == 0 || theme.linear) { - candidateBlockRect.size.height = NSMaxY(backgroundRect) - NSMinY(candidateBlockRect); + _candidateBlock.size.height = NSMaxY(backgroundRect) - NSMinY(_candidateBlock); } else { - candidateBlockRect.size.height += theme.linespace; + _candidateBlock.size.height += theme.linespace; } - candidateBlockRect = [self backingAlignedRect:NSIntersectionRect(candidateBlockRect, backgroundRect) - options:NSAlignAllEdgesNearest]; - candidateBlockPath = squirclePath(rectVertex(candidateBlockRect), - MIN(theme.highlightedCornerRadius, NSHeight(candidateBlockRect) * 0.5)); + _candidateBlock = [self backingAlignedRect:NSIntersectionRect(_candidateBlock, backgroundRect) + options:NSAlignAllEdgesNearest]; + candidateBlockPath = squirclePath(rectVertex(_candidateBlock), + MIN(theme.highlightedCornerRadius, NSHeight(_candidateBlock) * 0.5)); // Draw candidate highlight rect CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, @@ -1672,7 +1680,7 @@ - (void)updateLayer { CGFloat kerning = [theme.attrs[NSKernAttributeName] doubleValue]; if (theme.tabled) { gridPath = [NSBezierPath bezierPath]; - gridOriginY = NSMinY(candidateBlockRect); + gridOriginY = NSMinY(_candidateBlock); tabInterval = theme.separatorWidth * 2; } for (NSUInteger i = 0; i < _candidateRanges.count; ++i) { @@ -1695,7 +1703,7 @@ - (void)updateLayer { leadingRect.size.width += theme.separatorWidth; leadingRect.origin.y += _alignmentRectInsets.top - ceil(theme.linespace * 0.5); leadingRect.size.height += ceil(theme.linespace * 0.5); - leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, candidateBlockRect) + leadingRect = [self backingAlignedRect:NSIntersectionRect(leadingRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } if (NSIsEmptyRect(trailingRect)) { @@ -1705,27 +1713,27 @@ - (void)updateLayer { trailingRect.size.width += theme.separatorWidth; trailingRect.origin.y += _alignmentRectInsets.top; trailingRect.size.height += floor(theme.linespace * 0.5); - trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } if (!NSIsEmptyRect(bodyRect)) { bodyRect.origin.x += theme.borderInset.width; bodyRect.size.width += theme.separatorWidth; bodyRect.origin.y += _alignmentRectInsets.top; - bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } if (theme.tabled) { CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(candidateBlockRect)) > 2) { // horizontal border - [gridPath moveToPoint:NSMakePoint(NSMinX(candidateBlockRect) + + if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(_candidateBlock)) > 2) { // horizontal border + [gridPath moveToPoint:NSMakePoint(NSMinX(_candidateBlock) + theme.separatorWidth * 0.5, bottomEdge)]; - [gridPath lineToPoint:NSMakePoint(NSMaxX(candidateBlockRect) - + [gridPath lineToPoint:NSMakePoint(NSMaxX(_candidateBlock) - theme.separatorWidth * 0.5, bottomEdge)]; gridOriginY = bottomEdge; } CGPoint headOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; - if (headOrigin.x > NSMinX(candidateBlockRect) + theme.separatorWidth + kerning) { // vertical bar + if (headOrigin.x > NSMinX(_candidateBlock) + theme.separatorWidth + kerning) { // vertical bar [gridPath moveToPoint:NSMakePoint(headOrigin.x, headOrigin.y + cornerRadius * 0.8)]; [gridPath lineToPoint:NSMakePoint(headOrigin.x, NSMaxY(NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect) - cornerRadius * 0.8)]; } @@ -1733,14 +1741,24 @@ - (void)updateLayer { CGFloat tabPosition = ceil((tailEdge + kerning * 0.5 - theme.borderInset.width) / tabInterval) * tabInterval + theme.borderInset.width; if (!NSIsEmptyRect(trailingRect)) { trailingRect.size.width += tabPosition - tailEdge; - trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, candidateBlockRect) + trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } else if (NSIsEmptyRect(leadingRect)) { bodyRect.size.width += tabPosition - tailEdge; - bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, candidateBlockRect) + bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } } + NSRange labelRange; + NSString *toolTip = [_textStorage attribute:NSToolTipAttributeName + atIndex:candidateRange.location + effectiveRange:&labelRange]; + NSRect labelRect = [self blockRectForRange:labelRange]; + labelRect.origin = NSIsEmptyRect(leadingRect) ? bodyRect.origin : leadingRect.origin; + labelRect.size.width += theme.separatorWidth; + labelRect.size.height += ceil(theme.linespace * 0.5); + [self addToolTipRect:labelRect owner:toolTip userData:nil]; + NSBezierPath *candidatePath; // Handles the special case where containing boxes are separated if (NSIsEmptyRect(bodyRect) && !NSIsEmptyRect(leadingRect) && @@ -1763,14 +1781,25 @@ - (void)updateLayer { candidateRect.origin.x = backgroundRect.origin.x; candidateRect.origin.y += _alignmentRectInsets.top - ceil(theme.linespace * 0.5); candidateRect.size.height += theme.linespace; - candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, candidateBlockRect) + candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, _candidateBlock) options:NSAlignAllEdgesNearest]; _candidatePaths[i] = squirclePath(rectVertex(candidateRect), cornerRadius); + + CGFloat indent = [[_textStorage attribute:NSParagraphStyleAttributeName + atIndex:candidateRange.location + effectiveRange:NULL] headIndent]; + NSString *toolTip = [_textStorage attribute:NSToolTipAttributeName + atIndex:candidateRange.location + effectiveRange:NULL]; + NSRect labelRect = {candidateRect.origin, + {indent + theme.separatorWidth * 0.5, candidateRect.size.height}}; + [self addToolTipRect:labelRect owner:toolTip userData:nil]; } } } // Draw paging Rect + _pagingBlock = NSZeroRect; if (pagingRange.length > 0) { NSRect pageUpRect = [self blockRectForRange: NSMakeRange(pagingRange.location, 1)]; @@ -1787,15 +1816,15 @@ - (void)updateLayer { pageUpRect.size.height += theme.linespace; pageDownRect.origin.y -= ceil(theme.linespace * 0.5); pageDownRect.size.height += theme.linespace; - pageUpRect = NSIntersectionRect(pageUpRect, candidateBlockRect); - pageDownRect = NSIntersectionRect(pageDownRect, candidateBlockRect); + pageUpRect = NSIntersectionRect(pageUpRect, _candidateBlock); + pageDownRect = NSIntersectionRect(pageDownRect, _candidateBlock); } else { - NSRect pagingRect = NSMakeRect(NSMinX(backgroundRect), - NSMaxY(candidateBlockRect), - NSWidth(backgroundRect), - NSMaxY(backgroundRect) - NSMaxY(candidateBlockRect)); - pageUpRect = NSIntersectionRect(pageUpRect, pagingRect); - pageDownRect = NSIntersectionRect(pageDownRect, pagingRect); + _pagingBlock = NSMakeRect(NSMinX(backgroundRect), + NSMaxY(_candidateBlock), + NSWidth(backgroundRect), + NSMaxY(backgroundRect) - NSMaxY(_candidateBlock)); + pageUpRect = NSIntersectionRect(pageUpRect, _pagingBlock); + pageDownRect = NSIntersectionRect(pageDownRect, _pagingBlock); } pageUpRect = [self backingAlignedRect:pageUpRect options:NSAlignAllEdgesNearest]; @@ -1805,6 +1834,19 @@ - (void)updateLayer { MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) * 0.5); _pagingPaths[0] = squirclePath(rectVertex(pageUpRect), cornerRadius); _pagingPaths[1] = squirclePath(rectVertex(pageDownRect), cornerRadius); + [self addToolTipRect:pageUpRect owner:[_textStorage attribute:NSToolTipAttributeName + atIndex:pagingRange.location + effectiveRange:NULL] userData:nil]; + [self addToolTipRect:pageDownRect owner:[_textStorage attribute:NSToolTipAttributeName + atIndex:NSMaxRange(pagingRange) - 1 + effectiveRange:NULL] userData:nil]; + NSRect pageNumRect = NSMakeRect(NSMinX(pageUpRect), + NSMinY(pageUpRect), + NSMinX(pageDownRect) - NSMaxX(pageUpRect), + NSHeight(pageDownRect)); + [self addToolTipRect:pageNumRect owner:[_textStorage attribute:NSToolTipAttributeName + atIndex:pagingRange.location + 2 + effectiveRange:NULL] userData:nil]; } // Set layers @@ -1812,7 +1854,7 @@ - (void)updateLayer { [sourceOutFilter setDefaults]; _shape.path = panelPath.quartzPath; _shape.fillColor = NSColor.whiteColor.CGColor; - [self.layer setSublayers:nil]; + self.layer.sublayers = nil; CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; // border layer BOOL drawBorders = !NSEqualSizes(theme.borderInset, NSZeroSize) && theme.borderColor; @@ -1906,8 +1948,8 @@ - (void)updateLayer { - (NSUInteger)getIndexFromClickSpot:(NSPoint)spot { NSPoint point = [self convertPoint:spot fromView:nil]; if (NSPointInRect(point, self.bounds)) { - if (_deleteBackPath != nil && [_deleteBackPath containsPoint:point]) { - return kBackSpace; + if (NSPointInRect(point, _preeditBlock)) { + return [_deleteBackPath containsPoint:point] ? kBackSpace : kCodeInput; } if (_pagingPaths.count > 0) { if ([_pagingPaths[0] containsPoint:point]) { @@ -1933,6 +1975,7 @@ - (NSUInteger)getIndexFromClickSpot:(NSPoint)spot { @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; + NSScreen *_screen; NSSize _maxSize; CGFloat _textWidthLimit; @@ -1943,6 +1986,7 @@ @implementation SquirrelPanel { NSUInteger _numCandidates; NSUInteger _highlightedIndex; NSUInteger _functionButton; + NSUInteger _caretPos; BOOL _caretAtHome; BOOL _firstPage; BOOL _lastPage; @@ -1968,6 +2012,10 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } +- (SquirrelInputController *)inputController { + return SquirrelInputController.currentController; +} + - (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { SquirrelTheme *theme = [_view selectTheme:appear]; @@ -2071,12 +2119,13 @@ - (instancetype)init { backing:NSBackingStoreBuffered defer:YES]; if (self) { - self.level = CGWindowLevelForKey(kCGCursorWindowLevelKey) - 10; + self.level = CGWindowLevelForKey(kCGAssistiveTechHighWindowLevel) + 10; self.alphaValue = 1.0; self.hasShadow = NO; self.opaque = NO; self.backgroundColor = NSColor.clearColor; self.delegate = self; + self.allowsToolTipsWhenApplicationIsInactive = YES; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.bounds]; @@ -2094,50 +2143,63 @@ - (instancetype)init { [self updateDisplayParameters]; [self initializeUIStyleForAppearance:defaultAppear]; [self initializeUIStyleForAppearance:darkAppear]; - - [self addObserver:self - forKeyPath:@"screen" - options:NSKeyValueObservingOptionNew - context:@selector(updateDisplayParameters)]; } return self; } -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - if (context == @selector(updateDisplayParameters)) { - [self updateDisplayParameters]; +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { + if ([notification.object isMemberOfClass:SquirrelPanel.class]) { + [notification.object updateDisplayParameters]; } } - (void)sendEvent:(NSEvent *)event { NSUInteger cursorIndex; switch (event.type) { + case NSEventTypeLeftMouseDown: + if (event.clickCount == 1 && _functionButton == kCodeInput) { + NSPoint spot = [_view.textView convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; + NSUInteger inputIndex = [_view.textView characterIndexForInsertionAtPoint:spot]; + if (inputIndex == 0) { + [self.inputController perform:kSELECT onIndex:kHome]; + } else if (inputIndex < _caretPos) { + [self.inputController moveCursor:_caretPos + toPosition:inputIndex + inlinePreedit:NO + inlineCandidate:NO]; + } else if (inputIndex >= _view.preeditRange.length) { + [self.inputController perform:kSELECT onIndex:kEnd]; + } else if (inputIndex > _caretPos + 1) { + [self.inputController moveCursor:_caretPos + toPosition:inputIndex - 1 + inlinePreedit:NO + inlineCandidate:NO]; + } + } + break; case NSEventTypeLeftMouseUp: cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; if (event.clickCount == 1 && cursorIndex != NSNotFound && (cursorIndex == _highlightedIndex || cursorIndex == _functionButton)) { - [_inputController perform:kSELECT onIndex:cursorIndex]; + [self.inputController perform:kSELECT onIndex:cursorIndex]; } break; case NSEventTypeRightMouseUp: cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; if (event.clickCount == 1 && cursorIndex != NSNotFound) { if (cursorIndex == _highlightedIndex) { - [_inputController perform:kDELETE onIndex:cursorIndex]; + [self.inputController perform:kDELETE onIndex:cursorIndex]; } else if (cursorIndex == _functionButton) { switch (cursorIndex) { case kPageUp: - [_inputController perform:kSELECT onIndex:kHome]; + [self.inputController perform:kSELECT onIndex:kHome]; break; case kPageDown: - [_inputController perform:kSELECT onIndex:kEnd]; + [self.inputController perform:kSELECT onIndex:kEnd]; break; case kBackSpace: - [_inputController perform:kSELECT onIndex:kEscape]; + [self.inputController perform:kSELECT onIndex:kEscape]; break; } } @@ -2147,7 +2209,7 @@ - (void)sendEvent:(NSEvent *)event { cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlightedIndex != cursorIndex) { _highlightedIndex = cursorIndex; - [_inputController perform:kHILITE onIndex: + [self.inputController perform:kHILITE onIndex: [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || cursorIndex == kBackSpace) && _functionButton != cursorIndex) { @@ -2190,6 +2252,8 @@ - (void)sendEvent:(NSEvent *)event { } [_view highlightFunctionButton:cursorIndex]; [self display]; + } else if (cursorIndex == kCodeInput && _functionButton != cursorIndex) { + _functionButton = cursorIndex; } break; case NSEventTypeLeftMouseDragged: @@ -2203,30 +2267,30 @@ - (void)sendEvent:(NSEvent *)event { if (event.phase == NSEventPhaseBegan) { _scrollLocus = NSZeroPoint; } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && - _scrollLocus.x != NSNotFound && _scrollLocus.y != NSNotFound) { + !isnan(_scrollLocus.x) && !isnan(_scrollLocus.y)) { // determine scrolling direction by confining to sectors within ±30º of any axis - if (ABS(event.scrollingDeltaX) > ABS(event.scrollingDeltaY) * sqrt(3)) { + if (ABS(event.scrollingDeltaX) > ABS(event.scrollingDeltaY) * sqrt(3.0)) { _scrollLocus.x += event.scrollingDeltaX * (event.hasPreciseScrollingDeltas ? 1 : 10); - } else if (ABS(event.scrollingDeltaY) > ABS(event.scrollingDeltaX) * sqrt(3)) { + } else if (ABS(event.scrollingDeltaY) > ABS(event.scrollingDeltaX) * sqrt(3.0)) { _scrollLocus.y += event.scrollingDeltaY * (event.hasPreciseScrollingDeltas ? 1 : 10); } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT - onIndex:(theme.vertical ? kPageDown : kPageUp)]; - _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + [self.inputController perform:kSELECT + onIndex:(theme.vertical ? kPageDown : kPageUp)]; + _scrollLocus = NSMakePoint(NAN, NAN); } else if (_scrollLocus.y > scrollThreshold) { - [_inputController perform:kSELECT - onIndex:kPageUp]; - _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + [self.inputController perform:kSELECT + onIndex:kPageUp]; + _scrollLocus = NSMakePoint(NAN, NAN); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT - onIndex:(theme.vertical ? kPageUp : kPageDown)]; - _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + [self.inputController perform:kSELECT + onIndex:(theme.vertical ? kPageUp : kPageDown)]; + _scrollLocus = NSMakePoint(NAN, NAN); } else if (_scrollLocus.y < -scrollThreshold) { - [_inputController perform:kSELECT - onIndex:kPageDown]; - _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); + [self.inputController perform:kSELECT + onIndex:kPageDown]; + _scrollLocus = NSMakePoint(NAN, NAN); } } } break; @@ -2236,13 +2300,18 @@ - (void)sendEvent:(NSEvent *)event { } } -- (NSScreen *)screen { +- (void)updateScreen { for (NSScreen *screen in NSScreen.screens) { if (NSPointInRect(_IbeamRect.origin, screen.frame)) { - return screen; + _screen = screen; + return; } } - return NSScreen.mainScreen; + _screen = NSScreen.mainScreen; +} + +- (NSScreen *)screen { + return _screen; } - (void)updateDisplayParameters { @@ -2251,7 +2320,7 @@ - (void)updateDisplayParameters { _maxSize = NSZeroSize; // size limits on textContainer - NSRect screenRect = self.screen.visibleFrame; + NSRect screenRect = _screen.visibleFrame; SquirrelTheme *theme = _view.currentTheme; CGFloat textWidthRatio = MIN(0.8, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); @@ -2295,7 +2364,7 @@ - (void)show { NSEdgeInsets insets = _view.alignmentRectInsets; CGFloat textWidthRatio = MIN(0.8, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); - NSRect screenRect = self.screen.visibleFrame; + NSRect screenRect = _screen.visibleFrame; // the sweep direction of the client app changes the behavior of adjusting squirrel panel position BOOL sweepVertical = NSWidth(_IbeamRect) > NSHeight(_IbeamRect); @@ -2367,7 +2436,7 @@ - (void)show { // Make the right edge of candidate block fixed at the left of cursor windowRect.origin.x = NSMinX(_IbeamRect) + insets.top - NSWidth(windowRect); if (_view.preeditRange.length > 0) { - windowRect.origin.x += NSHeight([_view blockRectForRange:_view.preeditRange]); + windowRect.origin.x += NSHeight(_view.preeditBlock); } } else { if (NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect) < NSMinX(screenRect)) { @@ -2394,7 +2463,7 @@ - (void)show { } windowRect.origin.y = NSMinY(_IbeamRect) + insets.top - NSHeight(windowRect); if (_view.preeditRange.length > 0) { - windowRect.origin.y += NSHeight([_view blockRectForRange:_view.preeditRange]); + windowRect.origin.y += NSHeight(_view.preeditBlock); } } else { if (NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect) < NSMinY(screenRect)) { @@ -2428,8 +2497,8 @@ - (void)show { windowRect.origin.y += NSHeight(maxContentRect) - NSHeight(contentRect); windowRect.size.height -= NSHeight(maxContentRect) - NSHeight(contentRect); } - windowRect = [self.screen backingAlignedRect:NSIntersectionRect(windowRect, screenRect) - options:NSAlignAllEdgesNearest]; + windowRect = [_screen backingAlignedRect:NSIntersectionRect(windowRect, screenRect) + options:NSAlignAllEdgesNearest]; [self setFrame:windowRect display:YES]; // rotate the view, the core in vertical mode! @@ -2448,14 +2517,14 @@ - (void)show { if (theme.translucency > 0) { [_back setBoundsOrigin:NSZeroPoint]; _back.frame = viewRect; - _back.appearance = NSApp.effectiveAppearance; + _back.appearance = self.effectiveAppearance; _back.hidden = NO; } else { _back.hidden = YES; } self.alphaValue = theme.alpha; - [self orderFront:nil]; + [self orderFrontRegardless]; // reset to initial position after showing status message _initPosition = _statusMessage != nil; // voila ! @@ -2471,12 +2540,6 @@ - (void)hide { _initPosition = YES; } -- (void)dealloc { - [self removeObserver:self - forKeyPath:@"screen" - context:@selector(updateDisplayParameters)]; -} - - (BOOL)shouldBreakLineInsideRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; NSUInteger __block lineCount = 0; @@ -2551,8 +2614,13 @@ - (void)showPreedit:(NSString *)preedit highlightedIndex:(NSUInteger)highlightedIndex pageNum:(NSUInteger)pageNum lastPage:(BOOL)lastPage { + if (!NSIntersectsRect(_IbeamRect, _screen.frame)) { + [self updateScreen]; + [self updateDisplayParameters]; + } _numCandidates = candidates.count; _highlightedIndex = _numCandidates == 0 ? NSNotFound : highlightedIndex; + _caretPos = caretPos; _caretAtHome = caretPos == NSNotFound || (caretPos == selRange.location && selRange.location == 1); _firstPage = pageNum == 0; _lastPage = lastPage; @@ -2573,10 +2641,6 @@ - (void)showPreedit:(NSString *)preedit return; } - if (!NSIntersectsRect(_IbeamRect, self.screen.frame)) { - [self updateDisplayParameters]; - } - SquirrelTheme *theme = _view.currentTheme; _view.textView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical; if (theme.lineLength > 0) { @@ -2908,14 +2972,14 @@ - (void)hideStatus:(NSTimer *)timer { [self hide]; } -static inline NSColor * blendColors(NSColor *foregroundColor, - NSColor *backgroundColor) { +static inline NSColor *blendColors(NSColor *foregroundColor, + NSColor *backgroundColor) { return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:backgroundColor ? : NSColor.lightGrayColor] colorWithAlphaComponent:foregroundColor.alphaComponent]; } -static NSFontDescriptor * getFontDescriptor(NSString *fullname) { +static NSFontDescriptor *getFontDescriptor(NSString *fullname) { if (fullname.length == 0) { return nil; } @@ -2967,7 +3031,7 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return lineHeight; } -static NSFont * getTallestFont(NSArray *fonts, BOOL vertical) { +static NSFont *getTallestFont(NSArray *fonts, BOOL vertical) { NSFont *tallestFont; CGFloat maxHeight = 0.0; for (NSFont *font in fonts) { @@ -3403,6 +3467,9 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(NO); + labelAttrs[NSToolTipAttributeName] = NSLocalizedString(@"candidate", nil); + labelHighlightedAttrs[NSToolTipAttributeName] = NSLocalizedString(@"candidate", nil); + // CHROMATICS refinement translucency = translucency ? : @(0.0); if (translucency.doubleValue > 0 && !isNative && backColor != nil && diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 13f2f14e1..29151cdc6 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -9,3 +9,11 @@ Squirrel may be suffering a crash due to improper configurations. \ Revert previous modifications to see if the problem recurs."; "say_voice" = "Alex"; + +"candidate" = "Click a candidate to ⎆select.\nSecondary click to ⎌forget selected word."; +"delete" = "Click to ⌫Delete the input.\nSecondary click to ⎋Escape the composing."; +"escape" = "Cannot delete any further.\nSecondary click to ⎋Escape the composing."; +"page_up" = "Click to ⇞Page Up.\nSecondary click to jump to ↖Home."; +"home" = "Cannot page up any further.\nSecondary click to jump to ↖Home."; +"page_down" = "Click to ⇞Page Down.\nSecondary click to jump to ↘End."; +"end" = "Cannot page down any further.\nSecondary click to jump to ↘End."; diff --git a/zh-HK.lproj/Localizable.strings b/zh-HK.lproj/Localizable.strings index 04c993571..f7aad4be2 100644 --- a/zh-HK.lproj/Localizable.strings +++ b/zh-HK.lproj/Localizable.strings @@ -9,3 +9,11 @@ 「鼠鬚筆」可能由於設定不當而崩潰。\ 請嘗試回退先前的改動,然後查看問題是否仍然存在。"; "say_voice" = "Sinji"; + +"candidate" = "點按以⎆選取候選字。點按輔助按鈕以⎌清除所選的記憶字詞。"; +"delete" = "點按以⌫刪除輸入。點按輔助按鈕以⎋取消輸入。"; +"escape" = "無法再刪除。點按輔助按鈕以⎋取消輸入。"; +"page_up" = "點按以⇞向上翻頁。點按輔助按鈕以跳至↖起點。"; +"home" = "無法再向上翻頁。點按輔助按鈕以跳至↖起點。"; +"page_down" = "點按以⇟向下翻頁。點按輔助按鈕以跳至↘結尾。"; +"end" = "無法再向下翻頁。點按輔助按鈕以跳至↘結尾。"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index e6d965acb..034116cb7 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -9,3 +9,11 @@ “鼠须管”可能因错误设置而崩溃。\ 请尝试撤销之前的修改,然后查看问题是否仍旧存在。"; "say_voice" = "TingTing"; + +"candidate" = "点按以⎆选择候选字。辅助点按以⎌删除所选的记忆字词。"; +"delete" = "点按以⌫删除输入。辅助点按以⎋取消输入。"; +"escape" = "不能再删除。辅助点按以⎋取消输入。"; +"page_up" = "点按以⇞向上翻页。辅助点按以跳到↖开头。"; +"home" = "不能再向上翻页。辅助点按以跳到↖开头。"; +"page_down" = "点按以⇟向下翻页。辅助点按以跳到↘结尾。"; +"end" = "不能再向下翻页。辅助点按以跳到↘结尾。"; diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index 0cb0284f3..e1d1b2a91 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -9,3 +9,11 @@ 「鼠鬚管」可能因設定不當而崩潰。\ 請嘗試回退先前的修改,然後查看問題是否依然存在。"; "say_voice" = "MeiJia"; + +"candidate" = "點按來⎆選取候選字。點按輔助按鈕來⎌清除所選的記憶字詞。"; +"delete" = "點按來⌫刪除輸入。點按輔助按鈕來⎋取消輸入。"; +"escape" = "無法再刪除。點按輔助按鈕來⎋取消輸入。"; +"page_up" = "點按來⇞向上翻頁。點按輔助按鈕來跳至↖起始處。"; +"home" = "無法再向上翻頁。點按輔助按鈕來跳至↖起始處。"; +"page_down" = "點按來⇟向下翻頁。點按輔助按鈕來跳至↘結尾處。"; +"end" = "無法再向下翻頁。點按輔助按鈕來跳至↘結尾處。"; From 0f9d63d179138fb654d682bf650f016675015306 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Wed, 14 Feb 2024 06:52:11 +0100 Subject: [PATCH 162/164] Update librime --- librime | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/librime b/librime index 7bc943ddf..295cb2ab6 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit 7bc943ddf06d9674da6dfffd5695db3dfec0a8d5 +Subproject commit 295cb2ab68f89ee9d3237c7d4b8033bda3f3b635 From 70e5893e231de25c6758660906aea46aad8095ba Mon Sep 17 00:00:00 2001 From: groverlynn Date: Thu, 15 Feb 2024 20:35:22 +0100 Subject: [PATCH 163/164] formatting --- SquirrelApplicationDelegate.h | 20 +- SquirrelApplicationDelegate.m | 2 +- SquirrelConfig.h | 6 +- SquirrelInputController.h | 12 +- SquirrelInputController.m | 245 +++++---- SquirrelPanel.h | 12 +- SquirrelPanel.m | 827 +++++++++++++++++------------- en.lproj/Localizable.strings | 2 +- input_source.m | 2 +- zh-HK.lproj/Localizable.strings | 14 +- zh-Hans.lproj/Localizable.strings | 14 +- zh-Hant.lproj/Localizable.strings | 14 +- 12 files changed, 648 insertions(+), 522 deletions(-) diff --git a/SquirrelApplicationDelegate.h b/SquirrelApplicationDelegate.h index 08e101b2d..3755c7fd2 100644 --- a/SquirrelApplicationDelegate.h +++ b/SquirrelApplicationDelegate.h @@ -5,20 +5,20 @@ @class SquirrelPanel; @class SquirrelOptionSwitcher; -typedef enum { +// Note: the SquirrelApplicationDelegate is instantiated automatically as an outlet of NSApp's instance +@interface SquirrelApplicationDelegate : NSObject + +typedef NS_ENUM(NSUInteger, SquirrelNotificationPolicy) { kShowNotificationsNever = 0, kShowNotificationsWhenAppropriate = 1, kShowNotificationsAlways = 2 -} SquirrelNotificationPolicy; - -// Note: the SquirrelApplicationDelegate is instantiated automatically as an outlet of NSApp's instance -@interface SquirrelApplicationDelegate : NSObject +}; @property(nonatomic, weak) IBOutlet NSMenu *menu; @property(nonatomic, weak) IBOutlet SquirrelPanel *panel; @property(nonatomic, weak) IBOutlet id updater; -@property(nonatomic, readonly, strong) SquirrelConfig *config; +@property(nonatomic, strong, readonly) SquirrelConfig *config; @property(nonatomic, readonly) SquirrelNotificationPolicy showNotifications; - (IBAction)deploy:(id)sender; @@ -36,13 +36,15 @@ typedef enum { @property(nonatomic, readonly) BOOL problematicLaunchDetected; -@end +@end // SquirrelApplicationDelegate + @interface NSApplication (SquirrelApp) -@property(nonatomic, readonly, strong) SquirrelApplicationDelegate *squirrelAppDelegate; +@property(nonatomic, strong, readonly) SquirrelApplicationDelegate *squirrelAppDelegate; + +@end // NSApplication (SquirrelApp) -@end // also used in main.m extern void show_notification(const char *msg_text); diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index f28146d8e..81bd62669 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -116,7 +116,7 @@ static void notification_handler(void *context_object, RimeSessionId session_id, if ([app_delegate.panel.optionSwitcher containsOption:@(option_name)]) { if ([app_delegate.panel.optionSwitcher updateGroupState:@(message_value) ofOption:@(option_name)]) { - NSString *schemaId = [app_delegate panel].optionSwitcher.schemaId; + NSString *schemaId = app_delegate.panel.optionSwitcher.schemaId; [app_delegate loadSchemaSpecificLabels:schemaId]; [app_delegate loadSchemaSpecificSettings:schemaId withRimeSession:session_id]; diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 4329c4b58..9304a8057 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -1,8 +1,5 @@ #import -typedef NSDictionary SquirrelAppOptions; -typedef NSMutableDictionary SquirrelMutableAppOptions; - @interface SquirrelOptionSwitcher : NSObject @property(nonatomic, strong, readonly) NSString *schemaId; @@ -32,6 +29,9 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; @interface SquirrelConfig : NSObject +typedef NSDictionary SquirrelAppOptions; +typedef NSMutableDictionary SquirrelMutableAppOptions; + @property(nonatomic, readonly) BOOL isOpen; @property(nonatomic, strong) NSString *colorSpace; @property(nonatomic, strong, readonly) NSString *schemaId; diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 9ae167778..92c7bd6f3 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -1,11 +1,13 @@ #import #import -typedef enum { +@interface SquirrelInputController : IMKInputController + +typedef NS_ENUM(NSInteger, SquirrelAction) { kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape) kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A') kDELETE = 3 // only accepts indices in digits (int 1) -} SquirrelAction; +}; typedef NS_ENUM(NSUInteger, SquirrelIndex) { // 0 ... 9 are ordinal digits, used as (int) index @@ -21,9 +23,7 @@ typedef NS_ENUM(NSUInteger, SquirrelIndex) { kVoidSymbol = 0xffffff // XK_VoidSymbol }; -@interface SquirrelInputController : IMKInputController - -@property(nonatomic, class, readonly) SquirrelInputController *currentController; +@property(class, weak, readonly) SquirrelInputController *currentController; - (void)moveCursor:(NSUInteger)cursorPosition toPosition:(NSUInteger)targetPosition @@ -33,4 +33,4 @@ typedef NS_ENUM(NSUInteger, SquirrelIndex) { - (void)perform:(SquirrelAction)action onIndex:(SquirrelIndex)index; -@end +@end // SquirrelInputController diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 6f50f0405..48240825b 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -6,6 +6,7 @@ #import "macos_keycode.h" #import #import +#import #import #import @@ -44,6 +45,7 @@ @implementation SquirrelInputController { + (void)setCurrentController:(SquirrelInputController *)controller { _currentController = controller; + NSApp.squirrelAppDelegate.panel.IbeamRect = NSZeroRect; } + (SquirrelInputController *)currentController { @@ -51,17 +53,17 @@ + (SquirrelInputController *)currentController { } + (void)setDeactivationTimeForController:(SquirrelInputController *)controller { - [_controllerDeactivationTime setObject:NSDate.now forKey:controller]; -} - -+ (NSDate *)lastControllerDeactivationTime { - return [_controllerDeactivationTime objectForKey:_currentController]; + [_controllerDeactivationTime setObject:[[NSDate alloc] init] forKey:controller]; } + (void)removeDeactivationTimeForController:(SquirrelInputController *)controller { [_controllerDeactivationTime removeObjectForKey:controller]; } ++ (NSDate *)lastDeactivationTime { + return [_controllerDeactivationTime objectForKey:_currentController]; +} + /*! @method @abstract Receive incoming event @@ -196,7 +198,8 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index client:(id)sender { *keepTracking = NO; @autoreleasepool { - if ((!_inlinePreedit && !_inlineCandidate) || _caretPos == index || + if ((!_inlinePreedit && !_inlineCandidate) || + _composedString.length == 0 || _caretPos == index || (flags & NSEventModifierFlagDeviceIndependentFlagsMask)) { return NO; } @@ -205,7 +208,7 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index lineHeightRectangle:NULL][@"IMKBaseline"] pointValue]; NSPoint tail = [[sender attributesForCharacterIndex:markedRange.length - 1 lineHeightRectangle:NULL][@"IMKBaseline"] pointValue]; - if (point.x > tail.x || index >= markedRange.length -1) { + if (point.x > tail.x || index >= markedRange.length) { if (_inlineCandidate && !_inlinePreedit) { return NO; } @@ -282,6 +285,7 @@ - (BOOL)processKey:(int)rime_keycode return handled; } + - (void)moveCursor:(NSUInteger)cursorPosition toPosition:(NSUInteger)targetPosition inlinePreedit:(BOOL)inlinePreedit @@ -290,8 +294,10 @@ - (void)moveCursor:(NSUInteger)cursorPosition NSString *composition = !inlinePreedit && !inlineCandidate ? _composedString : _preeditString.string; RIME_STRUCT(RimeContext, ctx); if (cursorPosition > targetPosition) { - NSString *targetPrefix = [composition substringToIndex:targetPosition]; - NSString *prefix = [composition substringToIndex:cursorPosition]; + NSString *targetPrefix = [[composition substringToIndex:targetPosition] + stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSString *prefix = [[composition substringToIndex:cursorPosition] + stringByReplacingOccurrencesOfString:@" " withString:@""]; while (targetPrefix.length < prefix.length) { rime_get_api()->process_key(_session, vertical ? XK_Up : XK_Left, kControlMask); rime_get_api()->get_context(_session, &ctx); @@ -299,23 +305,28 @@ - (void)moveCursor:(NSUInteger)cursorPosition size_t length = ctx.composition.cursor_pos < ctx.composition.sel_end ? (size_t)ctx.composition.cursor_pos : strlen(ctx.commit_text_preview) - (inlinePreedit ? 0 : (size_t)(ctx.composition.cursor_pos - ctx.composition.sel_end)); - prefix = [[NSString alloc] initWithBytes:ctx.commit_text_preview - length:(NSUInteger)length - encoding:NSUTF8StringEncoding]; + prefix = [[[NSString alloc] initWithBytes:ctx.commit_text_preview + length:(NSUInteger)length + encoding:NSUTF8StringEncoding] + stringByReplacingOccurrencesOfString:@" " withString:@""]; } else { - prefix = [[NSString alloc] initWithBytes:ctx.composition.preedit - length:(NSUInteger)ctx.composition.cursor_pos - encoding:NSUTF8StringEncoding]; + prefix = [[[NSString alloc] initWithBytes:ctx.composition.preedit + length:(NSUInteger)ctx.composition.cursor_pos + encoding:NSUTF8StringEncoding] + stringByReplacingOccurrencesOfString:@" " withString:@""]; } rime_get_api()->free_context(&ctx); } } else if (cursorPosition < targetPosition) { - NSString *targetSuffix = [composition substringFromIndex:targetPosition]; - NSString *suffix = [composition substringFromIndex:cursorPosition]; + NSString *targetSuffix = [[composition substringFromIndex:targetPosition] + stringByReplacingOccurrencesOfString:@" " withString:@""]; + NSString *suffix = [[composition substringFromIndex:cursorPosition] + stringByReplacingOccurrencesOfString:@" " withString:@""]; while (targetSuffix.length < suffix.length) { rime_get_api()->process_key(_session, vertical ? XK_Down : XK_Right, kControlMask); rime_get_api()->get_context(_session, &ctx); - suffix = @(ctx.composition.preedit + ctx.composition.cursor_pos + (!inlinePreedit && !inlineCandidate ? 3 : 0)); + suffix = [@(ctx.composition.preedit + ctx.composition.cursor_pos + (!inlinePreedit && !inlineCandidate ? 3 : 0)) + stringByReplacingOccurrencesOfString:@" " withString:@""]; rime_get_api()->free_context(&ctx); } } @@ -324,7 +335,7 @@ - (void)moveCursor:(NSUInteger)cursorPosition - (void)perform:(SquirrelAction)action onIndex:(SquirrelIndex)index { - //NSLog(@"perform action: %u on index: %lu", action, (unsigned long)index); + //NSLog(@"perform action: %lu on index: %lu", action, index); bool handled = false; if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) { handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0); @@ -408,7 +419,7 @@ - (void)clearChord { - (NSUInteger)recognizedEvents:(id)sender { //NSLog(@"recognizedEvents:"); - return NSEventMaskKeyDown | NSEventMaskFlagsChanged | NSEventMaskLeftMouseDown; + return NSEventMaskKeyDown|NSEventMaskFlagsChanged|NSEventMaskLeftMouseDown; } NSString *getOptionLabel(RimeSessionId session, const char *option, Bool state) { @@ -446,6 +457,9 @@ - (void)showInitialStatus { NSString *foldedOptions = options.count == 0 ? schemaName : [NSString stringWithFormat:@"%@|%@", schemaName, [options componentsJoinedByString:@" "]]; [NSApp.squirrelAppDelegate.panel updateStatusLong:foldedOptions statusShort:schemaName]; + if (@available(macOS 14.0, *)) { + _lastModifiers |= NSEventModifierFlagHelp; + } [self rimeUpdate]; } } @@ -467,10 +481,10 @@ - (void)activateServer:(id)sender { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } + [SquirrelInputController removeDeactivationTimeForController:self]; if (NSApp.squirrelAppDelegate.showNotifications == kShowNotificationsAlways) { if (!SquirrelInputController.currentController || - (![SquirrelInputController.currentController isEqualTo:self] && - [SquirrelInputController.lastControllerDeactivationTime timeIntervalSinceNow] < -0.5)) { + SquirrelInputController.lastDeactivationTime.timeIntervalSinceNow < -1.0) { [self showInitialStatus]; } } @@ -493,9 +507,6 @@ - (instancetype)initWithServer:(IMKServer *)server if (self = [super initWithServer:server delegate:delegate client:inputClient]) { [self createSession]; - _preeditString = [[NSMutableAttributedString alloc] init]; - _originalString = [[NSMutableString alloc] init]; - _composedString = [[NSMutableString alloc] init]; } return self; } @@ -520,25 +531,18 @@ - (void)deactivateServer:(id)sender { - (void)commitComposition:(id)sender { //NSLog(@"commitComposition:"); + [self commitString:[self composedString:sender]]; + [self hidePalettes]; if (_session) { - [self commitString:[self composedString:sender]]; - [self hidePalettes]; rime_get_api()->clear_composition(_session); } } -- (void)inputControllerWillClose { - if (_session) { - [self destroySession]; - } - if ([SquirrelInputController.currentController isEqualTo:self]) { - [SquirrelInputController setCurrentController:nil]; - } - [SquirrelInputController removeDeactivationTimeForController:self]; +- (void)clearBuffer { + NSApp.squirrelAppDelegate.panel.IbeamRect = NSZeroRect; _preeditString = nil; _originalString = nil; _composedString = nil; - [super inputControllerWillClose]; } // a piece of comment from SunPinyin's macos wrapper says: @@ -593,10 +597,9 @@ - (void)hidePalettes { } - (void)dealloc { + //NSLog(@"dealloc"); [self destroySession]; - _preeditString = nil; - _originalString = nil; - _composedString = nil; + [self clearBuffer]; } - (NSRange)selectionRange { @@ -609,11 +612,11 @@ - (NSRange)replacementRange { - (void)commitString:(id)string { //NSLog(@"commitString:"); - [self.client insertText:string - replacementRange:self.replacementRange]; - _preeditString = nil; - _originalString = nil; - _composedString = nil; + if (string) { + [self.client insertText:string + replacementRange:self.replacementRange]; + } + [self clearBuffer]; } - (void)cancelComposition { @@ -665,18 +668,8 @@ - (void)showPreeditString:(NSString *)preedit [self updateComposition]; } -- (void)showPanelWithPreedit:(NSString *)preedit - selRange:(NSRange)selRange - caretPos:(NSUInteger)caretPos - candidates:(NSArray *)candidates - comments:(NSArray *)comments - highlightedIndex:(NSUInteger)highlightedIndex - pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage { - //NSLog(@"showPanelWithPreedit:...:"); - _candidates = candidates; - SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; - NSRect IbeamRect; +- (CGRect)getIbeamRect { + NSRect IbeamRect = NSZeroRect; [self.client attributesForCharacterIndex:0 lineHeightRectangle:&IbeamRect]; if (NSEqualRects(IbeamRect, NSZeroRect) && _preeditString.length == 0) { @@ -695,10 +688,15 @@ - (void)showPanelWithPreedit:(NSString *)preedit lineHeightRectangle:&IbeamRect]; } } + if (NSIsEmptyRect(IbeamRect)) { + return IbeamRect; + } NSWidth(IbeamRect) > NSHeight(IbeamRect) ? IbeamRect.origin.x += _inlineOffset : IbeamRect.origin.y += _inlineOffset; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view - if (_goodOldCapsLock && (_lastModifiers & NSEventModifierFlagCapsLock)) { + if ((_goodOldCapsLock && (_lastModifiers & NSEventModifierFlagCapsLock)) || + (_lastModifiers & NSEventModifierFlagHelp)) { + _lastModifiers &= ~NSEventModifierFlagHelp; NSRect screenRect = NSScreen.mainScreen.frame; if (NSIntersectsRect(IbeamRect, screenRect)) { screenRect = NSScreen.mainScreen.visibleFrame; @@ -722,17 +720,37 @@ - (void)showPanelWithPreedit:(NSString *)preedit } } } - panel.IbeamRect = IbeamRect; - [panel showPreedit:preedit - selRange:selRange - caretPos:caretPos - candidates:candidates - comments:comments - highlightedIndex:highlightedIndex - pageNum:pageNum - lastPage:lastPage]; + return IbeamRect; } +- (void)showPanelWithPreedit:(NSString *)preedit + selRange:(NSRange)selRange + caretPos:(NSUInteger)caretPos + candidates:(NSArray *)candidates + comments:(NSArray *)comments + highlightedIndex:(NSUInteger)highlightedIndex + pageNum:(NSUInteger)pageNum + lastPage:(BOOL)lastPage { + //NSLog(@"showPanelWithPreedit:...:"); + _candidates = candidates; + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; + panel.IbeamRect = [self getIbeamRect]; + if (NSIsEmptyRect(panel.IbeamRect) && panel.statusMessage.length > 0) { + [panel updateStatusLong:nil statusShort:nil]; + } else { + [panel showPreedit:preedit + selRange:selRange + caretPos:caretPos + candidates:candidates + comments:comments + highlightedIndex:highlightedIndex + pageNum:pageNum + lastPage:lastPage]; + } +} + +#pragma mark - Private methods + - (void)createSession { NSString *app = self.client.bundleIdentifier; //NSLog(@"createSession: %@", app); @@ -760,6 +778,7 @@ - (void)createSession { } _lastModifiers = 0; _lastEventCount = 0; + NSApp.squirrelAppDelegate.panel.IbeamRect = NSZeroRect; [self rimeUpdate]; } } @@ -790,8 +809,8 @@ - (BOOL)rimeConsumeCommittedText { return NO; } -NSUInteger UTF8LengthToUTF16Length(const char* cstring, int length) { - return [[NSString alloc] initWithBytes:cstring +NSUInteger inline UTF8LengthToUTF16Length(const char* string, int length) { + return [[NSString alloc] initWithBytes:string length:(NSUInteger)length encoding:NSUTF8StringEncoding].length; } @@ -856,52 +875,52 @@ - (void)rimeUpdate { NSUInteger length = UTF8LengthToUTF16Length(preedit, ctx.composition.length); NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; - if (!showingStatus) { - if (_showingSwitcherMenu) { - if (_inlinePlaceholder) { - [self updateComposition]; + if (showingStatus) { + [self clearBuffer]; + } else if (_showingSwitcherMenu) { + if (_inlinePlaceholder) { + [self updateComposition]; + } + } else if (_inlineCandidate) { + const char *candidatePreview = ctx.commit_text_preview; + NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; + if (_inlinePreedit) { + if (end <= caretPos && caretPos < length) { + candidatePreviewText = [candidatePreviewText stringByAppendingString: + [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; } - } else if (_inlineCandidate) { - const char *candidatePreview = ctx.commit_text_preview; - NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; - if (_inlinePreedit) { - if (end <= caretPos && caretPos < length) { - candidatePreviewText = [candidatePreviewText stringByAppendingString: - [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; - } - if (!didCommit || candidatePreviewText.length > 0) { - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) - caretPos:caretPos < end ? caretPos : candidatePreviewText.length - (length - caretPos)]; - } - } else { // preedit includes the soft cursor - if (end < caretPos && caretPos <= length) { - candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (caretPos - end)]; - } else if (caretPos < end && end < length) { - candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (length - end)]; - } - if (!didCommit || candidatePreviewText.length > 0) { - [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start - (caretPos < end), - candidatePreviewText.length - start + (caretPos < end)) - caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; - } + if (!didCommit || candidatePreviewText.length > 0) { + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) + caretPos:caretPos < end ? caretPos : candidatePreviewText.length - (length - caretPos)]; + } + } else { // preedit includes the soft cursor + if (end < caretPos && caretPos <= length) { + candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (caretPos - end)]; + } else if (caretPos < end && end < length) { + candidatePreviewText = [candidatePreviewText substringToIndex:candidatePreviewText.length - (length - end)]; + } + if (!didCommit || candidatePreviewText.length > 0) { + [self showPreeditString:candidatePreviewText + selRange:NSMakeRange(start - (caretPos < end), + candidatePreviewText.length - start + (caretPos < end)) + caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; + } + } + } else { + if (_inlinePreedit && !_showingSwitcherMenu) { + if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) { + [self showPlaceholder:kFullWidthSpace]; + } else if (!didCommit || preeditText.length > 0) { + [self showPreeditString:preeditText + selRange:NSMakeRange(start, end - start) + caretPos:caretPos]; } } else { - if (_inlinePreedit && !_showingSwitcherMenu) { - if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) { - [self showPlaceholder:kFullWidthSpace]; - } else if (!didCommit || preeditText.length > 0) { - [self showPreeditString:preeditText - selRange:NSMakeRange(start, end - start) - caretPos:caretPos]; - } - } else { - if (_inlinePlaceholder && preedit) { - [self showPlaceholder:kFullWidthSpace]; - } else if (!didCommit || preedit) { - [self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0]; - } + if (_inlinePlaceholder && preedit) { + [self showPlaceholder:kFullWidthSpace]; + } else if (!didCommit || preedit) { + [self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0]; } } } @@ -923,9 +942,7 @@ - (void)rimeUpdate { rime_get_api()->free_context(&ctx); } else { [self hidePalettes]; - _preeditString = nil; - _composedString = nil; - _originalString = nil; + [self clearBuffer]; } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 5c64de8a0..aed2cab76 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -4,13 +4,13 @@ @class SquirrelConfig; @class SquirrelOptionSwitcher; -typedef enum { +@interface SquirrelPanel : NSPanel + +typedef NS_ENUM(NSUInteger, SquirrelAppear) { defaultAppear = 0, lightAppear = 0, darkAppear = 1 -} SquirrelAppear; - -@interface SquirrelPanel : NSPanel +}; // Linear candidate list layout, as opposed to stacked candidate list layout. @property(nonatomic, readonly) BOOL linear; @@ -25,7 +25,7 @@ typedef enum { // Store switch options that change style (color theme) settings @property(nonatomic, strong) SquirrelOptionSwitcher *optionSwitcher; // Status message before pop-up is displayed; nil before normal panel is displayed -@property(nonatomic, readonly, strong) NSString *statusMessage; +@property(nonatomic, strong, readonly) NSString *statusMessage; // position of the text input I-beam cursor on screen. @property(nonatomic, assign) NSRect IbeamRect; @@ -49,4 +49,4 @@ typedef enum { - (void)loadLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update; -@end +@end // SquirrelPanel diff --git a/SquirrelPanel.m b/SquirrelPanel.m index 56efb8c28..b171b905b 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -24,7 +24,6 @@ - (CGPathRef)quartzPath { if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; - BOOL didClosePath = YES; for (NSInteger i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { case NSBezierPathElementMoveTo: @@ -32,29 +31,21 @@ - (CGPathRef)quartzPath { break; case NSBezierPathElementLineTo: CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); - didClosePath = NO; break; case NSBezierPathElementCurveTo: CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); - didClosePath = NO; break; case NSBezierPathElementQuadraticCurveTo: CGPathAddQuadCurveToPoint(path, NULL, points[0].x, points[0].y, points[1].x, points[1].y); - didClosePath = NO; break; case NSBezierPathElementClosePath: CGPathCloseSubpath(path); - didClosePath = YES; break; } } - // Be sure the path is closed or Quartz may not do valid hit detection. - if (!didClosePath) { - CGPathCloseSubpath(path); - } immutablePath = (CGPathRef)CFAutorelease(CGPathCreateCopy(path)); CGPathRelease(path); } @@ -315,11 +306,11 @@ - (NSColor *)invertLuminanceWithAdjustment:(NSInteger)sign { @interface SquirrelTheme : NSObject -typedef enum { +typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) { kStatusMessageTypeMixed = 0, kStatusMessageTypeShort = 1, kStatusMessageTypeLong = 2 -} SquirrelStatusMessageType; +}; @property(nonatomic, strong, readonly) NSColor *backColor; @property(nonatomic, strong, readonly) NSColor *highlightedCandidateBackColor; @@ -376,6 +367,8 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; +- (instancetype)init; + - (void) setBackColor:(NSColor *)backColor highlightedCandidateBackColor:(NSColor *)highlightedCandidateBackColor highlightedPreeditBackColor:(NSColor *)highlightedPreeditBackColor @@ -433,15 +426,86 @@ - (void)setAnnotationHeight:(CGFloat)height; @implementation SquirrelTheme +static inline NSColor *blendColors(NSColor *foregroundColor, NSColor *backgroundColor) { + return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:backgroundColor ? : NSColor.lightGrayColor] + colorWithAlphaComponent:foregroundColor.alphaComponent]; +} + +static NSFontDescriptor *getFontDescriptor(NSString *fullname) { + if (fullname.length == 0) { + return nil; + } + NSArray *fontNames = [fullname componentsSeparatedByString:@","]; + NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] + initWithCapacity:fontNames.count]; + for (NSString *fontName in fontNames) { + NSFont *font = [NSFont fontWithName:[fontName stringByTrimmingCharactersInSet: + NSCharacterSet.whitespaceAndNewlineCharacterSet] size:0.0]; + if (font != nil) { + // If the font name is not valid, NSFontDescriptor will still create something for us. + // However, when we draw the actual text, Squirrel will crash if there is any font descriptor + // with invalid font name. + NSFontDescriptor *fontDescriptor = font.fontDescriptor; + NSFontDescriptor *UIFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits: + NSFontDescriptorTraitUIOptimized]; + [validFontDescriptors addObject:[NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil ? + UIFontDescriptor : fontDescriptor]; + } + } + if (validFontDescriptors.count == 0) { + return nil; + } + NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; + NSFontDescriptor *emojiFontDescriptor = + [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; + NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange: + NSMakeRange(1, validFontDescriptors.count - 1)] + arrayByAddingObject:emojiFontDescriptor]; + return [initialFontDescriptor fontDescriptorByAddingAttributes: + @{NSFontCascadeListAttribute:fallbackDescriptors}]; +} + +static CGFloat getLineHeight(NSFont *font, BOOL vertical) { + if (vertical) { + font = font.verticalFont; + } + CGFloat lineHeight = ceil(font.ascender - font.descender); + NSArray *fallbackList = [font.fontDescriptor + objectForKey:NSFontCascadeListAttribute]; + for (NSFontDescriptor *fallback in fallbackList) { + NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback + size:font.pointSize]; + if (vertical) { + fallbackFont = fallbackFont.verticalFont; + } + lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); + } + return lineHeight; +} + +static NSFont *getTallestFont(NSArray *fonts, BOOL vertical) { + NSFont *tallestFont; + CGFloat maxHeight = 0.0; + for (NSFont *font in fonts) { + CGFloat fontHeight = getLineHeight(font, vertical); + if (fontHeight > maxHeight) { + tallestFont = font; + maxHeight = fontHeight; + } + } + return tallestFont; +} + static NSArray * formatLabels(NSAttributedString *format, NSArray *labels) { NSRange enumRange = NSMakeRange(0, 0); NSMutableArray *formatted = [[NSMutableArray alloc] initWithCapacity:labels.count]; NSCharacterSet *labelCharacters = [NSCharacterSet characterSetWithCharactersInString: - [labels componentsJoinedByString:@""]]; + [labels componentsJoinedByString:@""]]; if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xFF10, 10)] isSupersetOfSet:labelCharacters]) { // 01..9 - if ([format.string containsString:@"%c\u20E3"]) { // 1⃣..9⃣0⃣ + if ([format.string containsString:@"%c\u20E3"]) { // 1︎⃣..9︎⃣0︎⃣ enumRange = [format.string rangeOfString:@"%c\u20E3"]; for (NSString *label in labels) { const unichar chars[] = {[label characterAtIndex:0] - 0xFF10 + 0x0030, 0xFE0E, 0x20E3, 0x0}; @@ -454,7 +518,7 @@ @implementation SquirrelTheme enumRange = [format.string rangeOfString:@"%c\u20DD"]; for (NSString *label in labels) { const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0x24EA : - [label characterAtIndex:0] - 0xFF11 + 0x2460, 0x0}; + [label characterAtIndex:0] - 0xFF11 + 0x2460, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; @@ -464,7 +528,7 @@ @implementation SquirrelTheme enumRange = [format.string rangeOfString:@"(%c)"]; for (NSString *label in labels) { const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0x247D : - [label characterAtIndex:0] - 0xFF11 + 0x2474, 0x0}; + [label characterAtIndex:0] - 0xFF11 + 0x2474, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; @@ -474,8 +538,8 @@ @implementation SquirrelTheme enumRange = [format.string rangeOfString:@"%c."]; for (NSString *label in labels) { const unichar chars[] = {[label characterAtIndex:0] == 0xFF10 ? 0xD83C : - [label characterAtIndex:0] - 0xFF11 + 0x2488, - [label characterAtIndex:0] == 0xFF10 ? 0xDD00 : 0x0, 0x0}; + [label characterAtIndex:0] - 0xFF11 + 0x2488, + [label characterAtIndex:0] == 0xFF10 ? 0xDD00 : 0x0, 0x0}; NSMutableAttributedString *newFormat = format.mutableCopy; [newFormat replaceCharactersInRange:enumRange withString:[NSString stringWithFormat:@"%S", chars]]; @@ -533,6 +597,111 @@ @implementation SquirrelTheme return formatted; } ++ (NSColor *)secondaryTextColor { + if (@available(macOS 10.10, *)) { + return NSColor.secondaryLabelColor; + } else { + return NSColor.disabledControlTextColor; + } +} + ++ (NSColor *)accentColor { + if (@available(macOS 10.14, *)) { + return NSColor.controlAccentColor; + } else { + return [NSColor colorForControlTint:NSColor.currentControlTint]; + } +} + +- (instancetype)init { + if (self = [super init]) { + NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + paragraphStyle.alignment = NSTextAlignmentLeft; + // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left + // characters from setting the writing direction in case the label are direction-less symbols + paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; + + NSMutableParagraphStyle *preeditParagraphStyle = paragraphStyle.mutableCopy; + NSMutableParagraphStyle *pagingParagraphStyle = paragraphStyle.mutableCopy; + NSMutableParagraphStyle *statusParagraphStyle = paragraphStyle.mutableCopy; + + preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; + statusParagraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; + + NSFont *userFont = [NSFont fontWithDescriptor: + getFontDescriptor([NSFont userFontOfSize:0.0].fontName) + size:kDefaultFontSize]; + NSFont *userMonoFont = [NSFont fontWithDescriptor: + getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) + size:kDefaultFontSize]; + NSFont *monoDigitFont = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize + weight:NSFontWeightRegular]; + + NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; + attrs[NSForegroundColorAttributeName] = NSColor.controlTextColor; + attrs[NSFontAttributeName] = userFont; + // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. + attrs[NSWritingDirectionAttributeName] = @[@(0)]; + + NSMutableDictionary *highlightedAttrs = attrs.mutableCopy; + highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; + + NSMutableDictionary *labelAttrs = attrs.mutableCopy; + labelAttrs[NSForegroundColorAttributeName] = SquirrelTheme.accentColor; + labelAttrs[NSFontAttributeName] = userMonoFont; + + NSMutableDictionary *labelHighlightedAttrs = labelAttrs.mutableCopy; + labelHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; + + NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; + commentAttrs[NSForegroundColorAttributeName] = SquirrelTheme.secondaryTextColor; + commentAttrs[NSFontAttributeName] = userFont; + + NSMutableDictionary *commentHighlightedAttrs = commentAttrs.mutableCopy; + commentHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; + + NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; + preeditAttrs[NSForegroundColorAttributeName] = NSColor.textColor; + preeditAttrs[NSFontAttributeName] = userFont; + preeditAttrs[NSLigatureAttributeName] = @(0); + preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; + + NSMutableDictionary *preeditHighlightedAttrs = preeditAttrs.mutableCopy; + preeditHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedTextColor; + + NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; + pagingAttrs[NSFontAttributeName] = monoDigitFont; + pagingAttrs[NSForegroundColorAttributeName] = NSColor.controlTextColor; + + NSMutableDictionary *pagingHighlightedAttrs = pagingAttrs.mutableCopy; + pagingHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; + + NSMutableDictionary *statusAttrs = commentAttrs.mutableCopy; + statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; + + [self setAttrs:attrs + highlightedAttrs:highlightedAttrs + labelAttrs:labelAttrs + labelHighlightedAttrs:labelHighlightedAttrs + commentAttrs:commentAttrs + commentHighlightedAttrs:commentHighlightedAttrs + preeditAttrs:preeditAttrs + preeditHighlightedAttrs:preeditHighlightedAttrs + pagingAttrs:pagingAttrs + pagingHighlightedAttrs:pagingHighlightedAttrs + statusAttrs:statusAttrs]; + + [self setParagraphStyle:paragraphStyle + preeditParagraphStyle:preeditParagraphStyle + pagingParagraphStyle:pagingParagraphStyle + statusParagraphStyle:statusParagraphStyle]; + + [self setSelectKeys:@"12345" labels:@[@"1", @"2", @"3", @"4", @"5"] directUpdate:NO]; + [self setCandidateFormat:kDefaultCandidateFormat]; + } + return self; +} + - (void) setBackColor:(NSColor *)backColor highlightedCandidateBackColor:(NSColor *)highlightedCandidateBackColor highlightedPreeditBackColor:(NSColor *)highlightedPreeditBackColor @@ -619,7 +788,6 @@ - (void) setAttrs:(NSDictionary *)attrs @"Symbols/chevron.%@.circle.fill", _linear ? @"up" : @"left"]]; NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; attrsBackFill[NSAttachmentAttributeName] = attmBackFill; - attrsBackFill[NSToolTipAttributeName] = NSLocalizedString(@"page_up", nil); _symbolBackFill = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsBackFill]; @@ -628,7 +796,6 @@ - (void) setAttrs:(NSDictionary *)attrs @"Symbols/chevron.%@.circle", _linear ? @"up" : @"left"]]; NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; - attrsBackStroke[NSToolTipAttributeName] = NSLocalizedString(@"home", nil); _symbolBackStroke = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsBackStroke]; @@ -637,7 +804,6 @@ - (void) setAttrs:(NSDictionary *)attrs @"Symbols/chevron.%@.circle.fill", _linear ? @"down" : @"right"]]; NSMutableDictionary *attrsForwardFill = pagingAttrs.mutableCopy; attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; - attrsForwardFill[NSToolTipAttributeName] = NSLocalizedString(@"page_down", nil); _symbolForwardFill = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsForwardFill]; @@ -646,7 +812,6 @@ - (void) setAttrs:(NSDictionary *)attrs @"Symbols/chevron.%@.circle", _linear ? @"down" : @"right"]]; NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; - attrsForwardStroke[NSToolTipAttributeName] = NSLocalizedString(@"end", nil); _symbolForwardStroke = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsForwardStroke]; @@ -654,7 +819,6 @@ - (void) setAttrs:(NSDictionary *)attrs attmDeleteFill.image = [NSImage imageNamed:@"Symbols/delete.backward.fill"]; NSMutableDictionary *attrsDeleteFill = preeditAttrs.mutableCopy; attrsDeleteFill[NSAttachmentAttributeName] = attmDeleteFill; - attrsDeleteFill[NSToolTipAttributeName] = NSLocalizedString(@"delete", nil); attrsDeleteFill[NSVerticalGlyphFormAttributeName] = @(NO); _symbolDeleteFill = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsDeleteFill]; @@ -663,7 +827,6 @@ - (void) setAttrs:(NSDictionary *)attrs attmDeleteStroke.image = [NSImage imageNamed:@"Symbols/delete.backward"]; NSMutableDictionary *attrsDeleteStroke = preeditAttrs.mutableCopy; attrsDeleteStroke[NSAttachmentAttributeName] = attmDeleteStroke; - attrsDeleteStroke[NSToolTipAttributeName] = NSLocalizedString(@"escape", nil); attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @(NO); _symbolDeleteStroke = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsDeleteStroke]; @@ -774,9 +937,7 @@ - (void)setAnnotationHeight:(CGFloat)height { #pragma mark - Typesetting extensions for TextKit 1 (Mac OSX 10.9 to MacOS 11) @interface SquirrelLayoutManager : NSLayoutManager -@end - -@implementation SquirrelLayoutManager +@end @implementation SquirrelLayoutManager - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange atPoint:(NSPoint)origin { @@ -930,9 +1091,7 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutFragment : NSTextLayoutFragment -@end - -@implementation SquirrelTextLayoutFragment +@end @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { @@ -966,9 +1125,7 @@ - (void)drawAtPoint:(CGPoint)point API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutManager : NSTextLayoutManager -@end - -@implementation SquirrelTextLayoutManager +@end @implementation SquirrelTextLayoutManager - (BOOL) textLayoutManager:(NSTextLayoutManager *)textLayoutManager shouldBreakLineBeforeLocation:(id)location @@ -996,25 +1153,25 @@ - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutMan @interface SquirrelView : NSView -@property(nonatomic, readonly) NSTextView *textView; -@property(nonatomic, readonly) NSTextStorage *textStorage; -@property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; -@property(nonatomic, readonly) CAShapeLayer *shape; -@property(nonatomic, readonly) NSRect contentRect; -@property(nonatomic, readonly) NSRect preeditBlock; -@property(nonatomic, readonly) NSRect candidateBlock; -@property(nonatomic, readonly) NSRect pagingBlock; -@property(nonatomic, readonly) SquirrelAppear appear; -@property(nonatomic, readonly) NSEdgeInsets alignmentRectInsets; -@property(nonatomic, readonly) NSArray *candidateRanges; +@property(nonatomic, strong, readonly) NSTextView *textView; +@property(nonatomic, strong, readonly) NSTextStorage *textStorage; +@property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; +@property(nonatomic, strong, readonly) CAShapeLayer *shape; +@property(nonatomic, strong, readonly) NSMutableArray *candidatePaths; +@property(nonatomic, strong, readonly) NSMutableArray *pagingPaths; +@property(nonatomic, strong, readonly) NSBezierPath *deleteBackPath; +@property(nonatomic, strong, readonly) NSArray *candidateRanges; @property(nonatomic, readonly) NSRange preeditRange; @property(nonatomic, readonly) NSRange highlightedPreeditRange; @property(nonatomic, readonly) NSRange pagingRange; @property(nonatomic, readonly) NSUInteger highlightedIndex; @property(nonatomic, readonly) SquirrelIndex functionButton; -@property(nonatomic, readonly) NSBezierPath *deleteBackPath; -@property(nonatomic, readonly) NSMutableArray *candidatePaths; -@property(nonatomic, readonly) NSMutableArray *pagingPaths; +@property(nonatomic, readonly) NSRect contentRect; +@property(nonatomic, readonly) NSRect preeditBlock; +@property(nonatomic, readonly) NSRect candidateBlock; +@property(nonatomic, readonly) NSRect pagingBlock; +@property(nonatomic, readonly) NSEdgeInsets alignmentRectInsets; +@property(nonatomic, readonly) SquirrelAppear appear; - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(macos(12.0)); @@ -1036,7 +1193,7 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets - (void)highlightFunctionButton:(SquirrelIndex)functionButton; -- (NSUInteger)getIndexFromClickSpot:(NSPoint)spot; +- (NSUInteger)getIndexFromMouseSpot:(NSPoint)spot; @end @@ -1067,10 +1224,6 @@ - (SquirrelAppear)appear { return defaultAppear; } -- (BOOL)allowsVibrancy { - return YES; -} - - (SquirrelTheme *)selectTheme:(SquirrelAppear)appear { return appear == darkAppear ? _darkTheme : _defaultTheme; } @@ -1085,45 +1238,46 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.geometryFlipped = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; - } + self.layerUsesCoreImageFilters = YES; + + if (@available(macOS 12.0, *)) { + SquirrelTextLayoutManager *textLayoutManager = [[SquirrelTextLayoutManager alloc] init]; + textLayoutManager.usesFontLeading = NO; + textLayoutManager.usesHyphenation = NO; + textLayoutManager.delegate = textLayoutManager; + NSTextContainer *textContainer = [[NSTextContainer alloc] + initWithSize:NSZeroSize]; + textContainer.lineFragmentPadding = 0; + textLayoutManager.textContainer = textContainer; + NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; + [contentStorage addTextLayoutManager:textLayoutManager]; + _textView = [[NSTextView alloc] initWithFrame:frameRect + textContainer:textContainer]; + _textStorage = _textView.textContentStorage.textStorage; + } else { + SquirrelLayoutManager *layoutManager = [[SquirrelLayoutManager alloc] init]; + layoutManager.backgroundLayoutEnabled = YES; + layoutManager.usesFontLeading = NO; + layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; + layoutManager.delegate = layoutManager; + NSTextContainer *textContainer = [[NSTextContainer alloc] + initWithContainerSize:NSZeroSize]; + textContainer.lineFragmentPadding = 0; + [layoutManager addTextContainer:textContainer]; + _textStorage = [[NSTextStorage alloc] init]; + [_textStorage addLayoutManager:layoutManager]; + _textView = [[NSTextView alloc] initWithFrame:frameRect + textContainer:textContainer]; + } + _textView.drawsBackground = NO; + _textView.editable = NO; + _textView.selectable = NO; + _textView.wantsLayer = NO; - if (@available(macOS 12.0, *)) { - SquirrelTextLayoutManager *textLayoutManager = [[SquirrelTextLayoutManager alloc] init]; - textLayoutManager.usesFontLeading = NO; - textLayoutManager.usesHyphenation = NO; - textLayoutManager.delegate = textLayoutManager; - NSTextContainer *textContainer = [[NSTextContainer alloc] - initWithSize:NSZeroSize]; - textContainer.lineFragmentPadding = 0; - textLayoutManager.textContainer = textContainer; - NSTextContentStorage *contentStorage = [[NSTextContentStorage alloc] init]; - [contentStorage addTextLayoutManager:textLayoutManager]; - _textView = [[NSTextView alloc] initWithFrame:frameRect - textContainer:textContainer]; - _textStorage = _textView.textContentStorage.textStorage; - } else { - SquirrelLayoutManager *layoutManager = [[SquirrelLayoutManager alloc] init]; - layoutManager.backgroundLayoutEnabled = YES; - layoutManager.usesFontLeading = NO; - layoutManager.typesetterBehavior = NSTypesetterLatestBehavior; - layoutManager.delegate = layoutManager; - NSTextContainer *textContainer = [[NSTextContainer alloc] - initWithContainerSize:NSZeroSize]; - textContainer.lineFragmentPadding = 0; - [layoutManager addTextContainer:textContainer]; - _textStorage = [[NSTextStorage alloc] init]; - [_textStorage addLayoutManager:layoutManager]; - _textView = [[NSTextView alloc] initWithFrame:frameRect - textContainer:textContainer]; - } - _textView.drawsBackground = NO; - _textView.editable = NO; - _textView.selectable = NO; - _textView.wantsLayer = NO; - - _shape = [[CAShapeLayer alloc] init]; - _defaultTheme = [[SquirrelTheme alloc] init]; - _darkTheme = [[SquirrelTheme alloc] init]; + _shape = [[CAShapeLayer alloc] init]; + _defaultTheme = [[SquirrelTheme alloc] init]; + _darkTheme = [[SquirrelTheme alloc] init]; + } return self; } @@ -1370,7 +1524,6 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; _functionButton = kVoidSymbol; - [self removeAllToolTips]; // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round [self setNeedsDisplayInRect:self.bounds]; [self.textView setNeedsDisplayInRect:self.bounds]; @@ -1557,10 +1710,13 @@ - (void)updateLayer { NSRect backgroundRect = [self backingAlignedRect:NSInsetRect(panelRect, theme.borderInset.width, theme.borderInset.height) options:NSAlignAllEdgesNearest]; - NSBezierPath *panelPath = squirclePath(rectVertex(panelRect), - MIN(theme.cornerRadius, NSHeight(panelRect) * 0.5)); - NSBezierPath *backgroundPath = squirclePath(rectVertex(backgroundRect), - MIN(theme.highlightedCornerRadius, NSHeight(backgroundRect) * 0.5)); + CGFloat outerCornerRadius = MIN(theme.cornerRadius, NSHeight(panelRect) * 0.5); + CGFloat innerCornerRadius = MAX(MIN(theme.highlightedCornerRadius, NSHeight(backgroundRect) * 0.5), + outerCornerRadius - MIN(theme.borderInset.width, theme.borderInset.height)); + NSBezierPath *panelPath = squirclePath(rectVertex(panelRect), outerCornerRadius); + NSBezierPath *backgroundPath = squirclePath(rectVertex(backgroundRect), innerCornerRadius); + NSBezierPath *borderPath = [panelPath copy]; + [borderPath appendBezierPath:backgroundPath]; NSRange visibleRange; if (@available(macOS 12.0, *)) { @@ -1647,9 +1803,6 @@ - (void)updateLayer { deleteBackRect = [self backingAlignedRect:NSIntersectionRect(deleteBackRect, _preeditBlock) options:NSAlignAllEdgesNearest]; _deleteBackPath = squirclePath(rectVertex(deleteBackRect), cornerRadius); - [self addToolTipRect:deleteBackRect owner:[_textStorage attribute:NSToolTipAttributeName - atIndex:NSMaxRange(_preeditRange) - 1 - effectiveRange:NULL] userData:nil]; } // Draw candidate Rect @@ -1749,15 +1902,6 @@ - (void)updateLayer { options:NSAlignAllEdgesNearest]; } } - NSRange labelRange; - NSString *toolTip = [_textStorage attribute:NSToolTipAttributeName - atIndex:candidateRange.location - effectiveRange:&labelRange]; - NSRect labelRect = [self blockRectForRange:labelRange]; - labelRect.origin = NSIsEmptyRect(leadingRect) ? bodyRect.origin : leadingRect.origin; - labelRect.size.width += theme.separatorWidth; - labelRect.size.height += ceil(theme.linespace * 0.5); - [self addToolTipRect:labelRect owner:toolTip userData:nil]; NSBezierPath *candidatePath; // Handles the special case where containing boxes are separated @@ -1784,16 +1928,6 @@ - (void)updateLayer { candidateRect = [self backingAlignedRect:NSIntersectionRect(candidateRect, _candidateBlock) options:NSAlignAllEdgesNearest]; _candidatePaths[i] = squirclePath(rectVertex(candidateRect), cornerRadius); - - CGFloat indent = [[_textStorage attribute:NSParagraphStyleAttributeName - atIndex:candidateRange.location - effectiveRange:NULL] headIndent]; - NSString *toolTip = [_textStorage attribute:NSToolTipAttributeName - atIndex:candidateRange.location - effectiveRange:NULL]; - NSRect labelRect = {candidateRect.origin, - {indent + theme.separatorWidth * 0.5, candidateRect.size.height}}; - [self addToolTipRect:labelRect owner:toolTip userData:nil]; } } } @@ -1834,38 +1968,17 @@ - (void)updateLayer { MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) * 0.5); _pagingPaths[0] = squirclePath(rectVertex(pageUpRect), cornerRadius); _pagingPaths[1] = squirclePath(rectVertex(pageDownRect), cornerRadius); - [self addToolTipRect:pageUpRect owner:[_textStorage attribute:NSToolTipAttributeName - atIndex:pagingRange.location - effectiveRange:NULL] userData:nil]; - [self addToolTipRect:pageDownRect owner:[_textStorage attribute:NSToolTipAttributeName - atIndex:NSMaxRange(pagingRange) - 1 - effectiveRange:NULL] userData:nil]; - NSRect pageNumRect = NSMakeRect(NSMinX(pageUpRect), - NSMinY(pageUpRect), - NSMinX(pageDownRect) - NSMaxX(pageUpRect), - NSHeight(pageDownRect)); - [self addToolTipRect:pageNumRect owner:[_textStorage attribute:NSToolTipAttributeName - atIndex:pagingRange.location + 2 - effectiveRange:NULL] userData:nil]; } // Set layers - CIFilter *sourceOutFilter = [CIFilter filterWithName:@"CISourceOutCompositing"]; - [sourceOutFilter setDefaults]; _shape.path = panelPath.quartzPath; _shape.fillColor = NSColor.whiteColor.CGColor; self.layer.sublayers = nil; - CAShapeLayer *panelLayer = [[CAShapeLayer alloc] init]; - // border layer - BOOL drawBorders = !NSEqualSizes(theme.borderInset, NSZeroSize) && theme.borderColor; - panelLayer.path = panelPath.quartzPath; - panelLayer.fillColor = drawBorders ? theme.borderColor.CGColor : nil; - panelLayer.opacity = 1.0f - (float)theme.translucency; - [self.layer addSublayer:panelLayer]; - // background color layer - CAShapeLayer *backgroundLayer = [[CAShapeLayer alloc] init]; - backgroundLayer.path = backgroundPath.quartzPath; - backgroundLayer.fillColor = theme.backColor.CGColor; + // layers of large background elements + CALayer *BackLayers = [[CALayer alloc] init]; + BackLayers.opacity = 1.0f - (float)theme.translucency; + BackLayers.allowsGroupOpacity = YES; + [self.layer addSublayer:BackLayers]; // background image (pattern style) layer if (theme.backImage.valid) { CAShapeLayer *backImageLayer = [[CAShapeLayer alloc] init]; @@ -1877,75 +1990,103 @@ - (void)updateLayer { (backgroundPath.quartzPath, &transform)); backImageLayer.fillColor = [NSColor colorWithPatternImage:theme.backImage].CGColor; backImageLayer.affineTransform = CGAffineTransformInvert(transform); - backImageLayer.backgroundFilters = drawBorders ? @[sourceOutFilter] : nil; - [panelLayer addSublayer:backImageLayer]; - } else if (drawBorders) { - backgroundLayer.backgroundFilters = @[sourceOutFilter]; + [BackLayers addSublayer:backImageLayer]; } - [panelLayer addSublayer:backgroundLayer]; - if ((_preeditRange.length > 0 || (!theme.linear && _pagingRange.length > 0)) && + // background color layer + CAShapeLayer *backColorLayer = [[CAShapeLayer alloc] init]; + if ((!NSIsEmptyRect(_preeditBlock) || !NSIsEmptyRect(_pagingBlock)) && theme.preeditBackColor) { - backgroundLayer.fillColor = theme.preeditBackColor.CGColor; if (candidateBlockPath) { + NSBezierPath *nonCandidatePath = [backgroundPath copy]; + [nonCandidatePath appendBezierPath:candidateBlockPath]; + backColorLayer.path = nonCandidatePath.quartzPath; + backColorLayer.fillRule = kCAFillRuleEvenOdd; + backColorLayer.strokeColor = theme.preeditBackColor.CGColor; + backColorLayer.lineWidth = 0.5; + backColorLayer.fillColor = theme.preeditBackColor.CGColor; + [BackLayers addSublayer:backColorLayer]; + // candidate block's background color layer CAShapeLayer *candidateLayer = [[CAShapeLayer alloc] init]; candidateLayer.path = candidateBlockPath.quartzPath; candidateLayer.fillColor = theme.backColor.CGColor; - candidateLayer.backgroundFilters = @[sourceOutFilter]; - [backgroundLayer addSublayer:candidateLayer]; + [BackLayers addSublayer:candidateLayer]; + } else { + backColorLayer.path = backgroundPath.quartzPath; + backColorLayer.strokeColor = theme.preeditBackColor.CGColor; + backColorLayer.lineWidth = 0.5; + backColorLayer.fillColor = theme.preeditBackColor.CGColor; + [BackLayers addSublayer:backColorLayer]; } + } else { + backColorLayer.path = backgroundPath.quartzPath; + backColorLayer.strokeColor = theme.backColor.CGColor; + backColorLayer.lineWidth = 0.5; + backColorLayer.fillColor = theme.backColor.CGColor; + [BackLayers addSublayer:backColorLayer]; + } + // grids (in candidate block) layer + if (gridPath) { + CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; + gridLayer.path = gridPath.quartzPath; + gridLayer.lineWidth = 1.0; + gridLayer.strokeColor = [theme.commentAttrs[NSForegroundColorAttributeName] + blendedColorWithFraction:0.5 ofColor:theme.backColor].CGColor; + [BackLayers addSublayer:gridLayer]; } + // border layer + CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; + borderLayer.path = borderPath.quartzPath; + borderLayer.fillRule = kCAFillRuleEvenOdd; + borderLayer.fillColor = (theme.borderColor ? : theme.backColor).CGColor; + [BackLayers addSublayer:borderLayer]; + // layers of small highlighting elements + CALayer *ForeLayers = [[CALayer alloc] init]; + CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init]; + maskLayer.path = backgroundPath.quartzPath; + maskLayer.fillColor = NSColor.whiteColor.CGColor; + ForeLayers.mask = maskLayer; + [self.layer addSublayer:ForeLayers]; // highlighted preedit layer if (highlightedPreeditPath && theme.highlightedPreeditBackColor) { CAShapeLayer *highlightedPreeditLayer = [[CAShapeLayer alloc] init]; highlightedPreeditLayer.path = highlightedPreeditPath.quartzPath; highlightedPreeditLayer.fillColor = theme.highlightedPreeditBackColor.CGColor; - [self.layer addSublayer:highlightedPreeditLayer]; + [ForeLayers addSublayer:highlightedPreeditLayer]; } // highlighted candidate layer if (_highlightedIndex < _candidatePaths.count && theme.highlightedCandidateBackColor) { CAShapeLayer *highlightedCandidateLayer = [[CAShapeLayer alloc] init]; highlightedCandidateLayer.path = _candidatePaths[_highlightedIndex].quartzPath; highlightedCandidateLayer.fillColor = theme.highlightedCandidateBackColor.CGColor; - [self.layer addSublayer:highlightedCandidateLayer]; + [ForeLayers addSublayer:highlightedCandidateLayer]; } // function buttons (page up, page down, backspace) layer if (_functionButton != kVoidSymbol) { CAShapeLayer *functionButtonLayer = [self getFunctionButtonLayer]; if (functionButtonLayer) { - [self.layer addSublayer:functionButtonLayer]; + [ForeLayers addSublayer:functionButtonLayer]; } } - // grids (in candidate block) layer - if (gridPath) { - CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; - gridLayer.path = gridPath.quartzPath; - gridLayer.lineWidth = 1.0; - gridLayer.strokeColor = [[theme.commentAttrs[NSForegroundColorAttributeName] - blendedColorWithFraction:0.5 ofColor:theme.backColor] CGColor]; - [panelLayer addSublayer:gridLayer]; - } // logo at the beginning for status message - if (preeditRange.length == 0 && candidateBlockRange.length == 0) { + if (NSIsEmptyRect(_preeditBlock) && NSIsEmptyRect(_candidateBlock)) { CALayer *logoLayer = [[CALayer alloc] init]; CGFloat height = [theme.statusAttrs[NSParagraphStyleAttributeName] minimumLineHeight]; - NSRect logoRect = NSMakeRect(backgroundRect.origin.x, - backgroundRect.origin.y, - height, height); + NSRect logoRect = NSMakeRect(backgroundRect.origin.x, backgroundRect.origin.y, height, height); logoLayer.frame = [self backingAlignedRect:NSInsetRect(logoRect, -0.1 * height, -0.1 * height) options:NSAlignAllEdgesNearest]; NSImage *logoImage = [NSImage imageNamed:NSImageNameApplicationIcon]; logoImage.size = logoRect.size; CGFloat scaleFactor = [logoImage recommendedLayerContentsScale: - [self.window backingScaleFactor]]; + self.window.backingScaleFactor]; logoLayer.contents = logoImage; logoLayer.contentsScale = scaleFactor; logoLayer.affineTransform = theme.vertical ? CGAffineTransformMakeRotation(-M_PI_2) : CGAffineTransformIdentity; - [self.layer addSublayer:logoLayer]; + [ForeLayers addSublayer:logoLayer]; } } -- (NSUInteger)getIndexFromClickSpot:(NSPoint)spot { +- (NSUInteger)getIndexFromMouseSpot:(NSPoint)spot { NSPoint point = [self convertPoint:spot fromView:nil]; if (NSPointInRect(point, self.bounds)) { if (NSPointInRect(point, _preeditBlock)) { @@ -1970,12 +2111,107 @@ - (NSUInteger)getIndexFromClickSpot:(NSPoint)spot { @end // SquirrelView + +@interface SquirrelToolTip : NSWindow + +@property(nonatomic, weak, readonly) SquirrelPanel *panel; + +@end + +@implementation SquirrelToolTip { + NSVisualEffectView *_backView; + NSTextField *_textView; + NSTimer *_displayTimer; +} + +- (instancetype)initWithPanel:(SquirrelPanel *)panel { + self = [super initWithContentRect:NSZeroRect + styleMask:NSWindowStyleMaskNonactivatingPanel + backing:NSBackingStoreBuffered + defer:YES]; + if (self) { + _panel = panel; + self.level = panel.level + 1; + self.appearanceSource = panel; + self.backgroundColor = NSColor.clearColor; + self.opaque = YES; + self.hasShadow = YES; + NSView *contentView = [[NSView alloc] init]; + _backView = [[NSVisualEffectView alloc] init]; + _backView.material = NSVisualEffectMaterialToolTip; + [contentView addSubview:_backView]; + _textView = [[NSTextField alloc] init]; + _textView.bezeled = YES; + _textView.bezelStyle = NSTextFieldSquareBezel; + _textView.selectable = NO; + [contentView addSubview:_textView]; + self.contentView = contentView; + } + return self; +} + +- (void)showWithToolTip:(NSString *)toolTip { + if (toolTip.length == 0) { + [self hide]; + return; + } + + _textView.stringValue = toolTip; + _textView.font = [NSFont toolTipsFontOfSize:0]; + _textView.textColor = NSColor.windowFrameTextColor; + [_textView sizeToFit]; + NSSize contentSize = _textView.fittingSize; + + NSPoint spot = NSEvent.mouseLocation; + NSCursor *cursor = NSCursor.currentSystemCursor; + spot.x += cursor.image.size.width - cursor.hotSpot.x; + spot.y -= cursor.image.size.height - cursor.hotSpot.y; + NSRect windowRect = NSMakeRect(spot.x, spot.y - contentSize.height, contentSize.width, contentSize.height); + + NSRect screenRect = _panel.screen.visibleFrame; + if (NSMaxX(windowRect) > NSMaxX(screenRect)) { + windowRect.origin.x = NSMaxX(screenRect) - NSWidth(windowRect); + } + if (NSMinY(windowRect) < NSMinY(screenRect)) { + windowRect.origin.y = NSMinY(screenRect); + } + [self setFrame:[_panel.screen backingAlignedRect:windowRect + options:NSAlignAllEdgesNearest] + display:NO]; + _textView.frame = self.contentView.bounds; + _backView.frame = self.contentView.bounds; + + _displayTimer = [NSTimer scheduledTimerWithTimeInterval:kShowStatusDuration + target:self + selector:@selector(delayedDisplay:) + userInfo:nil + repeats:NO]; +} + +- (void)delayedDisplay:(NSTimer *)timer { + [self display]; + [self orderFrontRegardless]; +} + +- (void)hide { + if (_displayTimer) { + [_displayTimer invalidate]; + _displayTimer = nil; + } + if (self.visible) { + [self orderOut:nil]; + } +} + +@end // SquirrelToolTipView + #pragma mark - Panel window, dealing with text content and mouse interactions @implementation SquirrelPanel { SquirrelView *_view; NSVisualEffectView *_back; NSScreen *_screen; + SquirrelToolTip *_toolTip; NSSize _maxSize; CGFloat _textWidthLimit; @@ -2016,116 +2252,19 @@ - (SquirrelInputController *)inputController { return SquirrelInputController.currentController; } -- (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { - SquirrelTheme *theme = [_view selectTheme:appear]; - - NSMutableParagraphStyle *preeditParagraphStyle = [[NSMutableParagraphStyle alloc] init]; - NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; - NSMutableParagraphStyle *pagingParagraphStyle = [[NSMutableParagraphStyle alloc] init]; - NSMutableParagraphStyle *statusParagraphStyle = [[NSMutableParagraphStyle alloc] init]; - - preeditParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping; - statusParagraphStyle.lineBreakMode = NSLineBreakByTruncatingTail; - - preeditParagraphStyle.alignment = NSTextAlignmentLeft; - paragraphStyle.alignment = NSTextAlignmentLeft; - pagingParagraphStyle.alignment = NSTextAlignmentLeft; - statusParagraphStyle.alignment = NSTextAlignmentLeft; - - // Use left-to-right marks to declare the default writing direction and prevent strong right-to-left - // characters from setting the writing direction in case the label are direction-less symbols - preeditParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - pagingParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - statusParagraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight; - - NSFont *userFont = [NSFont fontWithDescriptor: - getFontDescriptor([NSFont userFontOfSize:0.0].fontName) - size:kDefaultFontSize]; - NSFont *userMonoFont = [NSFont fontWithDescriptor: - getFontDescriptor([NSFont userFixedPitchFontOfSize:0.0].fontName) - size:kDefaultFontSize]; - NSFont *monoDigitFont = [NSFont monospacedDigitSystemFontOfSize:kDefaultFontSize - weight:NSFontWeightRegular]; - - NSMutableDictionary *attrs = [[NSMutableDictionary alloc] init]; - attrs[NSForegroundColorAttributeName] = NSColor.controlTextColor; - attrs[NSFontAttributeName] = userFont; - // Use left-to-right embedding to prevent right-to-left text from changing the layout of the candidate. - attrs[NSWritingDirectionAttributeName] = @[@(0)]; - - NSMutableDictionary *highlightedAttrs = attrs.mutableCopy; - highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; - - NSMutableDictionary *labelAttrs = attrs.mutableCopy; - labelAttrs[NSForegroundColorAttributeName] = NSColor.controlAccentColor; - labelAttrs[NSFontAttributeName] = userMonoFont; - - NSMutableDictionary *labelHighlightedAttrs = labelAttrs.mutableCopy; - labelHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; - - NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; - commentAttrs[NSForegroundColorAttributeName] = NSColor.secondaryLabelColor; - commentAttrs[NSFontAttributeName] = userFont; - - NSMutableDictionary *commentHighlightedAttrs = commentAttrs.mutableCopy; - commentHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; - - NSMutableDictionary *preeditAttrs = [[NSMutableDictionary alloc] init]; - preeditAttrs[NSForegroundColorAttributeName] = NSColor.textColor; - preeditAttrs[NSFontAttributeName] = userFont; - preeditAttrs[NSLigatureAttributeName] = @(0); - preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle; - - NSMutableDictionary *preeditHighlightedAttrs = preeditAttrs.mutableCopy; - preeditHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedTextColor; - - NSMutableDictionary *pagingAttrs = [[NSMutableDictionary alloc] init]; - pagingAttrs[NSFontAttributeName] = theme.linear ? userMonoFont : monoDigitFont; - pagingAttrs[NSForegroundColorAttributeName] = theme.linear ? NSColor.controlAccentColor - : NSColor.controlTextColor; - - NSMutableDictionary *pagingHighlightedAttrs = pagingAttrs.mutableCopy; - pagingHighlightedAttrs[NSForegroundColorAttributeName] = theme.linear ? - NSColor.alternateSelectedControlTextColor : NSColor.selectedMenuItemTextColor; - - NSMutableDictionary *statusAttrs = commentAttrs.mutableCopy; - statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle; - - [theme setAttrs:attrs - highlightedAttrs:highlightedAttrs - labelAttrs:labelAttrs - labelHighlightedAttrs:labelHighlightedAttrs - commentAttrs:commentAttrs - commentHighlightedAttrs:commentHighlightedAttrs - preeditAttrs:preeditAttrs - preeditHighlightedAttrs:preeditHighlightedAttrs - pagingAttrs:pagingAttrs - pagingHighlightedAttrs:pagingHighlightedAttrs - statusAttrs:statusAttrs]; - - [theme setParagraphStyle:paragraphStyle - preeditParagraphStyle:preeditParagraphStyle - pagingParagraphStyle:pagingParagraphStyle - statusParagraphStyle:statusParagraphStyle]; - - [theme setSelectKeys:@"12345" labels:@[@"1", @"2", @"3", @"4", @"5"] directUpdate:NO]; - [theme setCandidateFormat:kDefaultCandidateFormat]; -} - - (instancetype)init { self = [super initWithContentRect:_IbeamRect styleMask:NSWindowStyleMaskNonactivatingPanel|NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; if (self) { - self.level = CGWindowLevelForKey(kCGAssistiveTechHighWindowLevel) + 10; + self.level = CGWindowLevelForKey(kCGCursorWindowLevelKey) - 100; self.alphaValue = 1.0; self.hasShadow = NO; self.opaque = NO; self.backgroundColor = NSColor.clearColor; self.delegate = self; - self.allowsToolTipsWhenApplicationIsInactive = YES; + self.acceptsMouseMovedEvents = YES; NSView *contentView = [[NSView alloc] init]; _view = [[SquirrelView alloc] initWithFrame:self.contentView.bounds]; @@ -2141,8 +2280,7 @@ - (instancetype)init { self.contentView = contentView; [self updateDisplayParameters]; - [self initializeUIStyleForAppearance:defaultAppear]; - [self initializeUIStyleForAppearance:darkAppear]; + _toolTip = [[SquirrelToolTip alloc] initWithPanel:self]; } return self; } @@ -2179,14 +2317,14 @@ - (void)sendEvent:(NSEvent *)event { } break; case NSEventTypeLeftMouseUp: - cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; if (event.clickCount == 1 && cursorIndex != NSNotFound && (cursorIndex == _highlightedIndex || cursorIndex == _functionButton)) { [self.inputController perform:kSELECT onIndex:cursorIndex]; } break; case NSEventTypeRightMouseUp: - cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; if (event.clickCount == 1 && cursorIndex != NSNotFound) { if (cursorIndex == _highlightedIndex) { [self.inputController perform:kDELETE onIndex:cursorIndex]; @@ -2206,9 +2344,13 @@ - (void)sendEvent:(NSEvent *)event { } break; case NSEventTypeMouseMoved: - cursorIndex = [_view getIndexFromClickSpot:self.mouseLocationOutsideOfEventStream]; + cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; + if (cursorIndex != _highlightedIndex && cursorIndex != _functionButton) { + [_toolTip hide]; + } if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlightedIndex != cursorIndex) { _highlightedIndex = cursorIndex; + [_toolTip showWithToolTip:NSLocalizedString(@"candidate", nil)]; [self.inputController perform:kHILITE onIndex: [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || @@ -2226,6 +2368,7 @@ - (void)sendEvent:(NSEvent *)event { range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; } cursorIndex = _firstPage ? kHome : kPageUp; + [_toolTip showWithToolTip:NSLocalizedString(_firstPage ? @"home" : @"page_up", nil)]; break; case kPageDown: [_view.textStorage addAttributes:theme.pagingAttrs @@ -2237,6 +2380,7 @@ - (void)sendEvent:(NSEvent *)event { range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; } cursorIndex = _lastPage ? kEnd : kPageDown; + [_toolTip showWithToolTip:NSLocalizedString(_lastPage ? @"end" : @"page_down", nil)]; break; case kBackSpace: [_view.textStorage addAttributes:theme.preeditHighlightedAttrs @@ -2248,6 +2392,7 @@ - (void)sendEvent:(NSEvent *)event { range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; } cursorIndex = _caretAtHome ? kEscape : kBackSpace; + [_toolTip showWithToolTip:NSLocalizedString(_caretAtHome ? @"escape" : @"delete", nil)]; break; } [_view highlightFunctionButton:cursorIndex]; @@ -2517,7 +2662,6 @@ - (void)show { if (theme.translucency > 0) { [_back setBoundsOrigin:NSZeroPoint]; _back.frame = viewRect; - _back.appearance = self.effectiveAppearance; _back.hidden = NO; } else { _back.hidden = YES; @@ -2535,6 +2679,7 @@ - (void)hide { [_statusTimer invalidate]; _statusTimer = nil; } + [_toolTip hide]; [self orderOut:nil]; _maxSize = NSZeroSize; _initPosition = YES; @@ -2605,6 +2750,45 @@ - (BOOL)shouldUseTabInRange:(NSRange)range } } +- (NSMutableAttributedString *)getPageNumString:(NSUInteger)pageNum { + SquirrelTheme *theme = _view.currentTheme; + if (!theme.vertical) { + return [[NSMutableAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] + attributes:theme.pagingAttrs]; + } + NSAttributedString *pageNumString =[[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:@"%lu", pageNum + 1] + attributes:theme.pagingAttrs]; + NSMutableDictionary *pageNumAttrs = [theme.pagingAttrs mutableCopy]; + NSFont *font = pageNumAttrs[NSFontAttributeName]; + CGFloat lineHeight = (theme.linear ? theme.paragraphStyle : theme.pagingParagraphStyle).minimumLineHeight; + CGFloat width = MAX(lineHeight, pageNumString.size.width); + NSImage *pageNumImage = [NSImage imageWithSize:NSMakeSize(lineHeight, width) + flipped:YES + drawingHandler:^BOOL(NSRect dstRect) { + CGContextRef context = NSGraphicsContext.currentContext.CGContext; + CGContextSaveGState(context); + CGContextTranslateCTM(context, lineHeight * 0.5 + font.ascender * 0.5 + font.descender * 0.5, width); + CGContextRotateCTM(context, -M_PI_2); + [pageNumString drawAtPoint:NSMakePoint(width * 0.5 - pageNumString.size.width * 0.5, -font.ascender)]; + CGContextRestoreGState(context); + return YES; + }]; + pageNumImage.resizingMode = NSImageResizingModeStretch; + pageNumImage.size = NSMakeSize(lineHeight, lineHeight); + NSTextAttachment *pageNumAttm = [[NSTextAttachment alloc] init]; + pageNumAttm.image = pageNumImage; + pageNumAttm.bounds = NSMakeRect(0, font.ascender * 0.5 + font.descender * 0.5 - lineHeight * 0.5, lineHeight, lineHeight); + NSMutableAttributedString *attmString = [[NSMutableAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %C ", (unichar)NSAttachmentCharacter] + attributes:pageNumAttrs]; + [attmString addAttribute:NSAttachmentAttributeName + value:pageNumAttm + range:NSMakeRange(1, 1)]; + return attmString; +} + // Main function to add attributes to text output from librime - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange @@ -2815,9 +2999,7 @@ - (void)showPreedit:(NSString *)preedit // paging indication if (theme.showPaging) { - NSMutableAttributedString *paging = [[NSMutableAttributedString alloc] - initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] - attributes:theme.pagingAttrs]; + NSMutableAttributedString *paging = [self getPageNumString:pageNum]; [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke atIndex:0]; [paging appendAttributedString:lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill]; @@ -2972,78 +3154,6 @@ - (void)hideStatus:(NSTimer *)timer { [self hide]; } -static inline NSColor *blendColors(NSColor *foregroundColor, - NSColor *backgroundColor) { - return [[foregroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction - ofColor:backgroundColor ? : NSColor.lightGrayColor] - colorWithAlphaComponent:foregroundColor.alphaComponent]; -} - -static NSFontDescriptor *getFontDescriptor(NSString *fullname) { - if (fullname.length == 0) { - return nil; - } - NSArray *fontNames = [fullname componentsSeparatedByString:@","]; - NSMutableArray *validFontDescriptors = [[NSMutableArray alloc] - initWithCapacity:fontNames.count]; - for (NSString *fontName in fontNames) { - NSFont *font = [NSFont fontWithName:[fontName stringByTrimmingCharactersInSet: - NSCharacterSet.whitespaceAndNewlineCharacterSet] size:0.0]; - if (font != nil) { - // If the font name is not valid, NSFontDescriptor will still create something for us. - // However, when we draw the actual text, Squirrel will crash if there is any font descriptor - // with invalid font name. - NSFontDescriptor *fontDescriptor = font.fontDescriptor; - NSFontDescriptor *UIFontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits: - NSFontDescriptorTraitUIOptimized]; - [validFontDescriptors addObject:[NSFont fontWithDescriptor:UIFontDescriptor size:0.0] != nil ? - UIFontDescriptor : fontDescriptor]; - } - } - if (validFontDescriptors.count == 0) { - return nil; - } - NSFontDescriptor *initialFontDescriptor = validFontDescriptors[0]; - NSFontDescriptor *emojiFontDescriptor = - [NSFontDescriptor fontDescriptorWithName:@"AppleColorEmoji" size:0.0]; - NSArray *fallbackDescriptors = [[validFontDescriptors subarrayWithRange: - NSMakeRange(1, validFontDescriptors.count - 1)] - arrayByAddingObject:emojiFontDescriptor]; - return [initialFontDescriptor fontDescriptorByAddingAttributes: - @{NSFontCascadeListAttribute:fallbackDescriptors}]; -} - -static CGFloat getLineHeight(NSFont *font, BOOL vertical) { - if (vertical) { - font = font.verticalFont; - } - CGFloat lineHeight = ceil(font.ascender - font.descender); - NSArray *fallbackList = [font.fontDescriptor - objectForKey:NSFontCascadeListAttribute]; - for (NSFontDescriptor *fallback in fallbackList) { - NSFont *fallbackFont = [NSFont fontWithDescriptor:fallback - size:font.pointSize]; - if (vertical) { - fallbackFont = fallbackFont.verticalFont; - } - lineHeight = MAX(lineHeight, ceil(fallbackFont.ascender - fallbackFont.descender)); - } - return lineHeight; -} - -static NSFont *getTallestFont(NSArray *fonts, BOOL vertical) { - NSFont *tallestFont; - CGFloat maxHeight = 0.0; - for (NSFont *font in fonts) { - CGFloat fontHeight = getLineHeight(font, vertical); - if (fontHeight > maxHeight) { - tallestFont = font; - maxHeight = fontHeight; - } - } - return tallestFont; -} - static void updateCandidateListLayout(BOOL *isLinear, BOOL *isTabled, SquirrelConfig *config, NSString *prefix) { NSString *candidateListLayout = [config getString: @@ -3390,10 +3500,10 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingHighlightedAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *zhFont = CFBridgingRelease(CTFontCreateForStringWithLanguage( - (CTFontRef)font, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); - NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForStringWithLanguage( - (CTFontRef)commentFont, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); + NSFont *zhFont = CFBridgingRelease(CTFontCreateForStringWithLanguage + ((CTFontRef)font, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); + NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForStringWithLanguage + ((CTFontRef)commentFont, CFSTR(" "), CFRangeMake(0, 1), CFSTR("zh"))); NSFont *refFont = getTallestFont(@[zhFont, labelFont, zhCommentFont], vertical); attrs[(NSString *)kCTBaselineReferenceInfoAttributeName] = @@ -3467,9 +3577,6 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); pagingHighlightedAttrs[NSVerticalGlyphFormAttributeName] = @(NO); - labelAttrs[NSToolTipAttributeName] = NSLocalizedString(@"candidate", nil); - labelHighlightedAttrs[NSToolTipAttributeName] = NSLocalizedString(@"candidate", nil); - // CHROMATICS refinement translucency = translucency ? : @(0.0); if (translucency.doubleValue > 0 && !isNative && backColor != nil && @@ -3495,9 +3602,8 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditBackColor = preeditBackColor ? : isNative ? NSColor.windowBackgroundColor : nil; textColor = textColor ? : NSColor.textColor; candidateTextColor = candidateTextColor ? : NSColor.controlTextColor; - commentTextColor = commentTextColor ? : NSColor.secondaryLabelColor; - candidateLabelColor = candidateLabelColor ? : isNative ? NSColor.controlAccentColor - : blendColors(candidateTextColor, backColor); + commentTextColor = commentTextColor ? : SquirrelTheme.secondaryTextColor; + candidateLabelColor = candidateLabelColor ? : isNative ? SquirrelTheme.accentColor : blendColors(candidateTextColor, backColor); highlightedBackColor = highlightedBackColor ? : isNative ? NSColor.selectedTextBackgroundColor : nil; highlightedTextColor = highlightedTextColor ? : NSColor.selectedTextColor; highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? NSColor.selectedContentBackgroundColor : nil; @@ -3567,3 +3673,4 @@ + (void)updateTheme:(SquirrelTheme *)theme } @end // SquirrelPanel + diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 29151cdc6..a2454b3a8 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -11,7 +11,7 @@ "say_voice" = "Alex"; "candidate" = "Click a candidate to ⎆select.\nSecondary click to ⎌forget selected word."; -"delete" = "Click to ⌫Delete the input.\nSecondary click to ⎋Escape the composing."; +"delete" = "Click to ⌫Delete the input by character.\nSecondary click to ⎋Escape the composing."; "escape" = "Cannot delete any further.\nSecondary click to ⎋Escape the composing."; "page_up" = "Click to ⇞Page Up.\nSecondary click to jump to ↖Home."; "home" = "Cannot page up any further.\nSecondary click to jump to ↖Home."; diff --git a/input_source.m b/input_source.m index d87176312..819c02ba0 100644 --- a/input_source.m +++ b/input_source.m @@ -26,7 +26,7 @@ void RegisterInputSource(void) { } } -void ActivateInputSource(int modes) { +void ActivateInputSource(RimeInputMode modes) { CFArrayRef sourceList = TISCreateInputSourceList(NULL, true); for (CFIndex i = 0; i < CFArrayGetCount(sourceList); ++i) { TISInputSourceRef inputSource = (TISInputSourceRef)CFArrayGetValueAtIndex(sourceList, i); diff --git a/zh-HK.lproj/Localizable.strings b/zh-HK.lproj/Localizable.strings index f7aad4be2..45818887d 100644 --- a/zh-HK.lproj/Localizable.strings +++ b/zh-HK.lproj/Localizable.strings @@ -10,10 +10,10 @@ 請嘗試回退先前的改動,然後查看問題是否仍然存在。"; "say_voice" = "Sinji"; -"candidate" = "點按以⎆選取候選字。點按輔助按鈕以⎌清除所選的記憶字詞。"; -"delete" = "點按以⌫刪除輸入。點按輔助按鈕以⎋取消輸入。"; -"escape" = "無法再刪除。點按輔助按鈕以⎋取消輸入。"; -"page_up" = "點按以⇞向上翻頁。點按輔助按鈕以跳至↖起點。"; -"home" = "無法再向上翻頁。點按輔助按鈕以跳至↖起點。"; -"page_down" = "點按以⇟向下翻頁。點按輔助按鈕以跳至↘結尾。"; -"end" = "無法再向下翻頁。點按輔助按鈕以跳至↘結尾。"; +"candidate" = "點按以⎆選取候選字。\n點按輔助按鈕以⎌清除所選的記憶字詞。"; +"delete" = "點按以逐字⌫刪除輸入。\n點按輔助按鈕以⎋取消輸入。"; +"escape" = "無法再刪除。\n點按輔助按鈕以⎋取消輸入。"; +"page_up" = "點按以⇞向上翻頁。\n點按輔助按鈕以跳至↖起點。"; +"home" = "無法再向上翻頁。\n點按輔助按鈕以跳至↖起點。"; +"page_down" = "點按以⇟向下翻頁。\n點按輔助按鈕以跳至↘結尾。"; +"end" = "無法再向下翻頁。\n點按輔助按鈕以跳至↘結尾。"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 034116cb7..cf655dc1b 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -10,10 +10,10 @@ 请尝试撤销之前的修改,然后查看问题是否仍旧存在。"; "say_voice" = "TingTing"; -"candidate" = "点按以⎆选择候选字。辅助点按以⎌删除所选的记忆字词。"; -"delete" = "点按以⌫删除输入。辅助点按以⎋取消输入。"; -"escape" = "不能再删除。辅助点按以⎋取消输入。"; -"page_up" = "点按以⇞向上翻页。辅助点按以跳到↖开头。"; -"home" = "不能再向上翻页。辅助点按以跳到↖开头。"; -"page_down" = "点按以⇟向下翻页。辅助点按以跳到↘结尾。"; -"end" = "不能再向下翻页。辅助点按以跳到↘结尾。"; +"candidate" = "点按以⎆选择候选字。\n辅助点按以⎌删除所选的记忆字词。"; +"delete" = "点按以逐字⌫删除输入。\n辅助点按以⎋取消输入。"; +"escape" = "不能再删除。\n辅助点按以⎋取消输入。"; +"page_up" = "点按以⇞向上翻页。\n辅助点按以跳到↖开头。"; +"home" = "不能再向上翻页。\n辅助点按以跳到↖开头。"; +"page_down" = "点按以⇟向下翻页。\n辅助点按以跳到↘结尾。"; +"end" = "不能再向下翻页。\n辅助点按以跳到↘结尾。"; diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index e1d1b2a91..3124322d8 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -10,10 +10,10 @@ 請嘗試回退先前的修改,然後查看問題是否依然存在。"; "say_voice" = "MeiJia"; -"candidate" = "點按來⎆選取候選字。點按輔助按鈕來⎌清除所選的記憶字詞。"; -"delete" = "點按來⌫刪除輸入。點按輔助按鈕來⎋取消輸入。"; -"escape" = "無法再刪除。點按輔助按鈕來⎋取消輸入。"; -"page_up" = "點按來⇞向上翻頁。點按輔助按鈕來跳至↖起始處。"; -"home" = "無法再向上翻頁。點按輔助按鈕來跳至↖起始處。"; -"page_down" = "點按來⇟向下翻頁。點按輔助按鈕來跳至↘結尾處。"; -"end" = "無法再向下翻頁。點按輔助按鈕來跳至↘結尾處。"; +"candidate" = "點按來⎆選取候選字。\n點按輔助按鈕來⎌清除所選的記憶字詞。"; +"delete" = "點按來逐字⌫刪除輸入。\n點按輔助按鈕來⎋取消輸入。"; +"escape" = "無法再刪除。\n點按輔助按鈕來⎋取消輸入。"; +"page_up" = "點按來⇞向上翻頁。\n點按輔助按鈕來跳至↖起始處。"; +"home" = "無法再向上翻頁。\n點按輔助按鈕來跳至↖起始處。"; +"page_down" = "點按來⇟向下翻頁。\n點按輔助按鈕來跳至↘結尾處。"; +"end" = "無法再向下翻頁。\n點按輔助按鈕來跳至↘結尾處。"; From 55bbde24c1b98d4d89ae1abd974c580584528252 Mon Sep 17 00:00:00 2001 From: groverlynn Date: Mon, 26 Feb 2024 04:58:08 +0100 Subject: [PATCH 164/164] expandable tabular --- .../chevron.down.symbolset/Contents.json | 15 + .../chevron.down.symbolset/chevron.down.svg | 160 +++ .../chevron.up.symbolset/Contents.json | 15 + .../chevron.up.symbolset/chevron.up.svg | 160 +++ .../Symbols/lock.fill.symbolset/Contents.json | 12 + .../Symbols/lock.fill.symbolset/lock.fill.svg | 160 +++ SquirrelApplicationDelegate.m | 21 +- SquirrelConfig.h | 38 +- SquirrelConfig.m | 86 +- SquirrelInputController.h | 37 +- SquirrelInputController.m | 142 +- SquirrelPanel.h | 12 +- SquirrelPanel.m | 1222 ++++++++++------- en.lproj/Localizable.strings | 5 +- zh-HK.lproj/Localizable.strings | 5 +- zh-Hans.lproj/Localizable.strings | 5 +- zh-Hant.lproj/Localizable.strings | 5 +- 17 files changed, 1480 insertions(+), 620 deletions(-) create mode 100644 Assets.xcassets/Symbols/chevron.down.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg create mode 100644 Assets.xcassets/Symbols/chevron.up.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg create mode 100644 Assets.xcassets/Symbols/lock.fill.symbolset/Contents.json create mode 100644 Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg diff --git a/Assets.xcassets/Symbols/chevron.down.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.down.symbolset/Contents.json new file mode 100644 index 000000000..e599b1e46 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.down.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg b/Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg new file mode 100644 index 000000000..26086ef64 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.down + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/chevron.up.symbolset/Contents.json b/Assets.xcassets/Symbols/chevron.up.symbolset/Contents.json new file mode 100644 index 000000000..3f9c92e18 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.symbolset/Contents.json @@ -0,0 +1,15 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "symbol-rendering-intent" : "template" + }, + "symbols" : [ + { + "filename" : "chevron.up.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg b/Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg new file mode 100644 index 000000000..e35a6e2d0 --- /dev/null +++ b/Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from chevron.up + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Assets.xcassets/Symbols/lock.fill.symbolset/Contents.json b/Assets.xcassets/Symbols/lock.fill.symbolset/Contents.json new file mode 100644 index 000000000..8d2370a08 --- /dev/null +++ b/Assets.xcassets/Symbols/lock.fill.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "symbols" : [ + { + "filename" : "lock.fill.svg", + "idiom" : "universal" + } + ] +} diff --git a/Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg b/Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg new file mode 100644 index 000000000..56b1c665e --- /dev/null +++ b/Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg @@ -0,0 +1,160 @@ + + + + + + + + Weight/Scale Variations + Ultralight + Thin + Light + Regular + Medium + Semibold + Bold + Heavy + Black + + + + + + + + + + + Design Variations + Symbols are supported in up to nine weights and three scales. + For optimal layout with text and other symbols, vertically align + symbols with the adjacent text. + + + + + + Margins + Leading and trailing margins on the left and right side of each symbol + can be adjusted by modifying the x-location of the margin guidelines. + Modifications are automatically applied proportionally to all + scales and weights. + + + + Exporting + Symbols should be outlined when exporting to ensure the + design is preserved when submitting to Xcode. + Template v.2.0 + Requires Xcode 12 or greater + Generated from lock.fill + Typeset at 100.0 points + Small + Medium + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 81bd62669..b35580828 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -174,21 +174,21 @@ - (void)shutdownRime { } NSArray *getScriptOptionsForSchema(SquirrelConfig *schema) { - NSUInteger numSwitches = [schema getListSize:@"switches"]; + NSUInteger numSwitches = [schema getListSizeForOption:@"switches"]; if (numSwitches == 0) { return nil; } for (NSUInteger i = 0; i < numSwitches; ++i) { - NSString *name = [schema getString:[NSString stringWithFormat: - @"switches/@%lu/name", i]]; + NSString *name = [schema getStringForOption:[NSString stringWithFormat: + @"switches/@%lu/name", i]]; if (name) { if ([name isEqualToString:@"simplification"] || [name isEqualToString:@"traditional"]) { return @[name]; } } else { - NSArray *options = [schema getList:[NSString stringWithFormat: - @"switches/@%lu/options", i]]; + NSArray *options = [schema getListForOption:[NSString stringWithFormat: + @"switches/@%lu/options", i]]; if ([options containsObject:@"simplification"] || [options containsObject:@"traditional"]) { return options; @@ -228,7 +228,7 @@ - (void)loadSettings { return; } - NSString *showNotificationsWhen = [_config getString:@"show_notifications_when"]; + NSString *showNotificationsWhen = [_config getStringForOption:@"show_notifications_when"]; if ([showNotificationsWhen isEqualToString:@"never"]) { _showNotifications = kShowNotificationsNever; } else if ([showNotificationsWhen isEqualToString:@"appropriate"]) { @@ -236,8 +236,7 @@ - (void)loadSettings { } else { _showNotifications = kShowNotificationsAlways; } - [self.panel loadConfig:_config forAppearance:defaultAppear]; - [self.panel loadConfig:_config forAppearance:darkAppear]; + [self.panel loadConfig:_config]; } - (void)loadSchemaSpecificSettings:(NSString *)schemaId @@ -251,12 +250,10 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId [schema hasSection:@"style"]) { SquirrelOptionSwitcher *optionSwitcher = [schema getOptionSwitcher]; self.panel.optionSwitcher = updateOptionSwitcher(optionSwitcher, sessionId); - [self.panel loadConfig:schema forAppearance:defaultAppear]; - [self.panel loadConfig:schema forAppearance:darkAppear]; + [self.panel loadConfig:schema]; } else { self.panel.optionSwitcher = [[SquirrelOptionSwitcher alloc] initWithSchemaId:schemaId]; - [self.panel loadConfig:self.config forAppearance:defaultAppear]; - [self.panel loadConfig:self.config forAppearance:darkAppear]; + [self.panel loadConfig:self.config]; } [schema close]; } diff --git a/SquirrelConfig.h b/SquirrelConfig.h index 9304a8057..8d2ac7f5e 100644 --- a/SquirrelConfig.h +++ b/SquirrelConfig.h @@ -45,25 +45,31 @@ typedef NSMutableDictionary SquirrelMutableAppOptions; - (BOOL)hasSection:(NSString *)section; -- (BOOL)getBool:(NSString *)option; -- (int)getInt:(NSString *)option; -- (double)getDouble:(NSString *)option; -- (double)getDouble:(NSString *)option - applyConstraint:(double(*)(double param))func; -- (NSNumber *)getOptionalBool:(NSString *)option; -- (NSNumber *)getOptionalInt:(NSString *)option; -- (NSNumber *)getOptionalDouble:(NSString *)option; -- (NSNumber *)getOptionalDouble:(NSString *)option - applyConstraint:(double(*)(double param))func; - -- (NSString *)getString:(NSString *)option; +- (BOOL)setBool:(bool)value forOption:(NSString *)option; +- (BOOL)setInt:(int)value forOption:(NSString *)option; +- (BOOL)setDouble:(double)value forOption:(NSString *)option; +- (BOOL)setString:(NSString *)value forOption:(NSString *)option; + +- (BOOL)getBoolForOption:(NSString *)option; +- (int)getIntForOption:(NSString *)option; +- (double)getDoubleForOption:(NSString *)option; +- (double)getDoubleForOption:(NSString *)option + applyConstraint:(double(*)(double param))func; + +- (NSNumber *)getOptionalBoolForOption:(NSString *)option; +- (NSNumber *)getOptionalIntForOption:(NSString *)option; +- (NSNumber *)getOptionalDoubleForOption:(NSString *)option; +- (NSNumber *)getOptionalDoubleForOption:(NSString *)option + applyConstraint:(double(*)(double param))func; + +- (NSString *)getStringForOption:(NSString *)option; // 0xaabbggrr or 0xbbggrr -- (NSColor *)getColor:(NSString *)option; +- (NSColor *)getColorForOption:(NSString *)option; // file path (absolute or relative to ~/Library/Rime) -- (NSImage *)getImage:(NSString *)option; +- (NSImage *)getImageForOption:(NSString *)option; -- (NSUInteger)getListSize:(NSString *)option; -- (NSArray *)getList:(NSString *)option; +- (NSUInteger)getListSizeForOption:(NSString *)option; +- (NSArray *)getListForOption:(NSString *)option; - (SquirrelOptionSwitcher *)getOptionSwitcher; - (SquirrelAppOptions *)getAppOptions:(NSString *)appName; diff --git a/SquirrelConfig.m b/SquirrelConfig.m index 016880659..2a0a9721d 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -137,25 +137,41 @@ - (BOOL)hasSection:(NSString *)section { return NO; } -- (BOOL)getBool:(NSString *)option { - return [self getOptionalBool:option].boolValue; +- (BOOL)setBool:(bool)value forOption:(NSString *)option { + return (BOOL)(rime_get_api()->config_set_bool(&_config, option.UTF8String, value)); } -- (int)getInt:(NSString *)option { - return [self getOptionalInt:option].intValue; +- (BOOL)setInt:(int)value forOption:(NSString *)option { + return (BOOL)(rime_get_api()->config_set_int(&_config, option.UTF8String, value)); } -- (double)getDouble:(NSString *)option { - return [self getOptionalDouble:option].doubleValue; +- (BOOL)setDouble:(double)value forOption:(NSString *)option { + return (BOOL)(rime_get_api()->config_set_double(&_config, option.UTF8String, value)); } -- (double)getDouble:(NSString *)option - applyConstraint:(double(*)(double param))func { - NSNumber *value = [self getOptionalDouble:option]; +- (BOOL)setString:(NSString *)value forOption:(NSString *)option { + return (BOOL)(rime_get_api()->config_set_string(&_config, option.UTF8String, value.UTF8String)); +} + +- (BOOL)getBoolForOption:(NSString *)option { + return [self getOptionalBoolForOption:option].boolValue; +} + +- (int)getIntForOption:(NSString *)option { + return [self getOptionalIntForOption:option].intValue; +} + +- (double)getDoubleForOption:(NSString *)option { + return [self getOptionalDoubleForOption:option].doubleValue; +} + +- (double)getDoubleForOption:(NSString *)option + applyConstraint:(double(*)(double param))func { + NSNumber *value = [self getOptionalDoubleForOption:option]; return func(value.doubleValue); } -- (NSNumber *)getOptionalBool:(NSString *)option { +- (NSNumber *)getOptionalBoolForOption:(NSString *)option { NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(BOOL) forKey:option]; if (cachedValue) { return cachedValue; @@ -166,10 +182,10 @@ - (NSNumber *)getOptionalBool:(NSString *)option { [_cache setObject:number forKey:option]; return number; } - return [_baseConfig getOptionalBool:option]; + return [_baseConfig getOptionalBoolForOption:option]; } -- (NSNumber *)getOptionalInt:(NSString *)option { +- (NSNumber *)getOptionalIntForOption:(NSString *)option { NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(int) forKey:option]; if (cachedValue) { return cachedValue; @@ -180,10 +196,10 @@ - (NSNumber *)getOptionalInt:(NSString *)option { [_cache setObject:number forKey:option]; return number; } - return [_baseConfig getOptionalInt:option]; + return [_baseConfig getOptionalIntForOption:option]; } -- (NSNumber *)getOptionalDouble:(NSString *)option { +- (NSNumber *)getOptionalDoubleForOption:(NSString *)option { NSNumber *cachedValue = [self cachedValueOfObjCType:@encode(double) forKey:option]; if (cachedValue) { return cachedValue; @@ -194,16 +210,16 @@ - (NSNumber *)getOptionalDouble:(NSString *)option { [_cache setObject:number forKey:option]; return number; } - return [_baseConfig getOptionalDouble:option]; + return [_baseConfig getOptionalDoubleForOption:option]; } -- (NSNumber *)getOptionalDouble:(NSString *)option - applyConstraint:(double(*)(double param))func { - NSNumber *value = [self getOptionalDouble:option]; +- (NSNumber *)getOptionalDoubleForOption:(NSString *)option + applyConstraint:(double(*)(double param))func { + NSNumber *value = [self getOptionalDoubleForOption:option]; return value ? [NSNumber numberWithDouble:func(value.doubleValue)] : nil; } -- (NSString *)getString:(NSString *)option { +- (NSString *)getStringForOption:(NSString *)option { NSString *cachedValue = [self cachedValueOfClass:NSString.class forKey:option]; if (cachedValue) { return cachedValue; @@ -216,47 +232,47 @@ - (NSString *)getString:(NSString *)option { [_cache setObject:string forKey:option]; return string; } - return [_baseConfig getString:option]; + return [_baseConfig getStringForOption:option]; } -- (NSColor *)getColor:(NSString *)option { +- (NSColor *)getColorForOption:(NSString *)option { NSColor *cachedValue = [self cachedValueOfClass:NSColor.class forKey:option]; if (cachedValue) { return cachedValue; } - NSColor *color = [self colorFromString:[self getString:option]]; + NSColor *color = [self colorFromString:[self getStringForOption:option]]; if (color) { [_cache setObject:color forKey:option]; return color; } - return [_baseConfig getColor:option]; + return [_baseConfig getColorForOption:option]; } -- (NSImage *)getImage:(NSString *)option { +- (NSImage *)getImageForOption:(NSString *)option { NSImage *cachedValue = [self cachedValueOfClass:NSImage.class forKey:option]; if (cachedValue) { return cachedValue; } - NSImage *image = [self imageFromFile:[self getString:option]]; + NSImage *image = [self imageFromFile:[self getStringForOption:option]]; if (image) { [_cache setObject:image forKey:option]; return image; } - return [_baseConfig getImage:option]; + return [_baseConfig getImageForOption:option]; } -- (NSUInteger)getListSize:(NSString *)option { +- (NSUInteger)getListSizeForOption:(NSString *)option { return rime_get_api()->config_list_size(&_config, option.UTF8String); } -- (NSArray *)getList:(NSString *)option { +- (NSArray *)getListForOption:(NSString *)option { RimeConfigIterator iterator; if (!rime_get_api()->config_begin_list(&iterator, &_config, option.UTF8String)) { return nil; } NSMutableArray *strList = [[NSMutableArray alloc] init]; while (rime_get_api()->config_next(&iterator)) { - [strList addObject:[self getString:@(iterator.path)]]; + [strList addObject:[self getStringForOption:@(iterator.path)]]; } rime_get_api()->config_end(&iterator); return strList; @@ -270,8 +286,8 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { NSMutableDictionary *switcher = [[NSMutableDictionary alloc] init]; NSMutableDictionary *optionGroups = [[NSMutableDictionary alloc] init]; while (rime_get_api()->config_next(&switchIter)) { - int reset = [self getInt:[@(switchIter.path) stringByAppendingString:@"/reset"]]; - NSString *name = [self getString:[@(switchIter.path) stringByAppendingString:@"/name"]]; + int reset = [self getIntForOption:[@(switchIter.path) stringByAppendingString:@"/reset"]]; + NSString *name = [self getStringForOption:[@(switchIter.path) stringByAppendingString:@"/name"]]; if (name) { if ([self hasSection:[@"style/!" stringByAppendingString:name]] || [self hasSection:[@"style/" stringByAppendingString:name]]) { @@ -287,7 +303,7 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { NSMutableArray *optionGroup = [[NSMutableArray alloc] init]; BOOL hasStyleSection = NO; while (rime_get_api()->config_next(&optionIter)) { - NSString *option = [self getString:@(optionIter.path)]; + NSString *option = [self getStringForOption:@(optionIter.path)]; [optionGroup addObject:option]; hasStyleSection |= [self hasSection:[@"style/" stringByAppendingString:option]]; } @@ -315,9 +331,9 @@ - (SquirrelAppOptions *)getAppOptions:(NSString *)appName { } while (rime_get_api()->config_next(&iterator)) { //NSLog(@"DEBUG option[%d]: %s (%s)", iterator.index, iterator.key, iterator.path); - NSNumber *value = [self getOptionalBool:@(iterator.path)] ? : - [self getOptionalInt:@(iterator.path)] ? : - [self getOptionalDouble:@(iterator.path)]; + NSNumber *value = [self getOptionalBoolForOption:@(iterator.path)] ? : + [self getOptionalIntForOption:@(iterator.path)] ? : + [self getOptionalDoubleForOption:@(iterator.path)]; if (value) { appOptions[@(iterator.key)] = value; } diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 92c7bd6f3..30dc575c6 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -3,24 +3,33 @@ @interface SquirrelInputController : IMKInputController +// kPROCESS accepts miscellaneous / function keys (e.g. XK_Escape) +// The remaining 3 actions accept candidate indices (int), starting from item 0 on page 0 typedef NS_ENUM(NSInteger, SquirrelAction) { - kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape) - kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A') - kDELETE = 3 // only accepts indices in digits (int 1) + kPROCESS = 0, + kSELECT = 1, + kHIGHLIGHT = 2, + kDELETE = 3 }; typedef NS_ENUM(NSUInteger, SquirrelIndex) { - // 0 ... 9 are ordinal digits, used as (int) index - // 0x21 ... 0x7e are ASCII chars (as selection keys) - // other rime keycodes (as function keys), for paging etc. - kBackSpace = 0xff08, // XK_BackSpace - kEscape = 0xff1b, // XK_Escape - kCodeInput = 0xff37, // XK_Codeinput - kHome = 0xff50, // XK_Home - kPageUp = 0xff55, // XK_Page_Up - kPageDown = 0xff56, // XK_Page_Down - kEnd = 0xff57, // XK_End - kVoidSymbol = 0xffffff // XK_VoidSymbol + // 0, 1, 2 ... are ordinal digits, used as (int) indices + // 0xFFXX are rime keycodes (as function keys), for paging etc. + kBackSpaceKey = 0xff08, // XK_BackSpace + kEscapeKey = 0xff1b, // XK_Escape + kCodeInputArea = 0xff37, // XK_Codeinput + kHomeKey = 0xff50, // XK_Home + kLeftKey = 0xff51, // XK_Left + kUpKey = 0xff52, // XK_Up + kRightKey = 0xff53, // XK_Right + kDownKey = 0xff54, // XK_Down + kPageUpKey = 0xff55, // XK_Page_Up + kPageDownKey = 0xff56, // XK_Page_Down + kEndKey = 0xff57, // XK_End + kExpandButton = 0xff04, + kCompressButton = 0xff05, + kLockButton = 0xff06, + kVoidSymbol = 0xffffff // XK_VoidSymbol }; @property(class, weak, readonly) SquirrelInputController *currentController; diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 48240825b..d4d610f29 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -10,7 +10,7 @@ #import #import -const int N_KEY_ROLL_OVER = 50; +static const int N_KEY_ROLL_OVER = 50; static NSString *const kFullWidthSpace = @" "; @implementation SquirrelInputController { @@ -22,6 +22,7 @@ @implementation SquirrelInputController { NSArray *_candidates; NSEventModifierFlags _lastModifiers; uint _lastEventCount; + NSUInteger _lastPageNum; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; @@ -41,7 +42,8 @@ @implementation SquirrelInputController { } static SquirrelInputController *_currentController = nil; -static NSMapTable *_controllerDeactivationTime = NSMapTable.weakToWeakObjectsMapTable; +static NSMapTable *_controllerDeactivationTime = + NSMapTable.weakToWeakObjectsMapTable; + (void)setCurrentController:(SquirrelInputController *)controller { _currentController = controller; @@ -212,9 +214,9 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index if (_inlineCandidate && !_inlinePreedit) { return NO; } - [self perform:kSELECT onIndex:kEnd]; + [self perform:kPROCESS onIndex:kEndKey]; } else if (point.x < head.x || index <= 0) { - [self perform:kSELECT onIndex:kHome]; + [self perform:kPROCESS onIndex:kHomeKey]; } else { [self moveCursor:_caretPos toPosition:index @@ -240,17 +242,30 @@ void set_CapsLock_LED_state(bool target_state) { - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers { + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; // with linear candidate list, arrow keys may behave differently. - Bool is_linear = (Bool)NSApp.squirrelAppDelegate.panel.linear; + Bool is_linear = (Bool)panel.linear; if (is_linear != rime_get_api()->get_option(_session, "_linear")) { rime_get_api()->set_option(_session, "_linear", is_linear); } // with vertical text, arrow keys may behave differently. - Bool is_vertical = (Bool)NSApp.squirrelAppDelegate.panel.vertical; + Bool is_vertical = (Bool)panel.vertical; if (is_vertical != rime_get_api()->get_option(_session, "_vertical")) { rime_get_api()->set_option(_session, "_vertical", is_vertical); } + if (panel.tabular && !rime_modifiers && + (is_vertical ? rime_keycode == XK_Left || rime_keycode == XK_Right + : rime_keycode == XK_Up || rime_keycode == XK_Down)) { + NSUInteger newIndex = [panel candidateIndexOnDirection:(SquirrelIndex)rime_keycode]; + if (newIndex != NSNotFound) { + if (!panel.locked && !panel.expanded && rime_keycode == (is_vertical ? XK_Left : XK_Down)) { + panel.expanded = YES; + } + return rime_get_api()->highlight_candidate(_session, newIndex); + } + } + BOOL handled = (BOOL)rime_get_api()->process_key(_session, rime_keycode, rime_modifiers); //NSLog(@"rime_keycode: 0x%x, rime_modifiers: 0x%x, handled = %d", rime_keycode, rime_modifiers, handled); @@ -337,24 +352,26 @@ - (void)perform:(SquirrelAction)action onIndex:(SquirrelIndex)index { //NSLog(@"perform action: %lu on index: %lu", action, index); bool handled = false; - if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) { - handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0); - } else if (index >= 0xff08 && index <= 0xffff && action == kSELECT) { - handled = rime_get_api()->process_key(_session, (int)index, 0); - } else if (index >= 0 && index < 10) { - switch (action) { - case kDELETE: - handled = rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index); - break; - case kSELECT: - handled = rime_get_api()->select_candidate_on_current_page(_session, (size_t)index); - break; - case kHILITE: - handled = rime_get_api()->highlight_candidate_on_current_page(_session, (size_t)index); - break; - } + switch (action) { + case kPROCESS: + if (index >= 0xff08 && index <= 0xffff) { + handled = rime_get_api()->process_key(_session, (int)index, 0); + } else if (index >= kExpandButton && index <= kLockButton) { + handled = true; + } + break; + case kSELECT: + handled = rime_get_api()->select_candidate(_session, index); + break; + case kHIGHLIGHT: + handled = rime_get_api()->highlight_candidate(_session, index); + break; + case kDELETE: + handled = rime_get_api()->delete_candidate(_session, index); + break; } if (handled) { + _lastPageNum = NSNotFound; [self rimeUpdate]; } } @@ -398,7 +415,7 @@ - (void)updateChord:(int)keycode } _chordDuration = 0.1; NSNumber *duration = [NSApp.squirrelAppDelegate.config - getOptionalDouble:@"chord_duration"]; + getOptionalDoubleForOption:@"chord_duration"]; if (duration.doubleValue > 0) { _chordDuration = duration.doubleValue; } @@ -467,7 +484,7 @@ - (void)showInitialStatus { - (void)activateServer:(id)sender { //NSLog(@"activateServer:"); NSString *keyboardLayout = [NSApp.squirrelAppDelegate.config - getString:@"keyboard_layout"]; + getStringForOption:@"keyboard_layout"]; if ([keyboardLayout isEqualToString:@"last"] || [keyboardLayout isEqualToString:@""]) { keyboardLayout = nil; @@ -493,7 +510,7 @@ - (void)activateServer:(id)sender { SquirrelConfig *defaultConfig = [[SquirrelConfig alloc] init]; if ([defaultConfig openWithConfigId:@"default"] && [defaultConfig hasSection:@"ascii_composer"]) { - _goodOldCapsLock = [defaultConfig getBool: + _goodOldCapsLock = [defaultConfig getBoolForOption: @"ascii_composer/good_old_caps_lock"]; } [defaultConfig close]; @@ -733,6 +750,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit lastPage:(BOOL)lastPage { //NSLog(@"showPanelWithPreedit:...:"); _candidates = candidates; + _lastPageNum = pageNum; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; panel.IbeamRect = [self getIbeamRect]; if (NSIsEmptyRect(panel.IbeamRect) && panel.statusMessage.length > 0) { @@ -819,6 +837,7 @@ - (void)rimeUpdate { //NSLog(@"rimeUpdate"); BOOL didCommit = [self rimeConsumeCommittedText]; + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; RIME_STRUCT(RimeStatus, status); if (rime_get_api()->get_status(_session, &status)) { // enable schema specific ui style @@ -830,11 +849,9 @@ - (void)rimeUpdate { [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId withRimeSession:_session]; // inline preedit - _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && - !rime_get_api()->get_option(_session, "no_inline")) || + _inlinePreedit = (panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) || rime_get_api()->get_option(_session, "inline"); - _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && - !rime_get_api()->get_option(_session, "no_inline")); + _inlineCandidate = panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline"); // if not inline, embed soft cursor in preedit string rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit); } else { @@ -846,9 +863,10 @@ - (void)rimeUpdate { RIME_STRUCT(RimeContext, ctx); if (rime_get_api()->get_context(_session, &ctx)) { - BOOL showingStatus = NSApp.squirrelAppDelegate.panel.statusMessage.length > 0; + BOOL showingStatus = panel.statusMessage.length > 0; // update raw input const char *raw_input = rime_get_api()->get_input(_session); + BOOL didCompose = ![_originalString isEqualToString:raw_input ? @(raw_input) : @""]; _originalString = raw_input ? @(raw_input) : @""; // update preedit text @@ -873,7 +891,31 @@ - (void)rimeUpdate { NSUInteger end = UTF8LengthToUTF16Length(preedit, ctx.composition.sel_end); NSUInteger caretPos = UTF8LengthToUTF16Length(preedit, ctx.composition.cursor_pos); NSUInteger length = UTF8LengthToUTF16Length(preedit, ctx.composition.length); - NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates; + NSUInteger numCandidates = (NSUInteger)ctx.menu.num_candidates; + NSUInteger pageNum = (NSUInteger)ctx.menu.page_no; + NSUInteger pageSize = (NSUInteger)ctx.menu.page_size; + NSUInteger highlightedIndex = numCandidates == 0 ? NSNotFound : + (NSUInteger)ctx.menu.highlighted_candidate_index; + BOOL isLastPage = (BOOL)ctx.menu.is_last_page; + + // update discloser and active line status in gridded layout + if (panel.tabular && !showingStatus && numCandidates > 0) { + if (didCompose) { + panel.activePage = 0; + } else if (_lastPageNum != NSNotFound) { + if (!panel.locked && panel.expanded && (pageNum | _lastPageNum | highlightedIndex) == 0) { + panel.expanded = NO; + } else if (!panel.locked && !panel.expanded && pageNum > 0 && pageNum > _lastPageNum) { + panel.expanded = YES; + } + if (panel.expanded && pageNum > _lastPageNum && panel.activePage < 4) { + panel.activePage = MIN(panel.activePage + pageNum - _lastPageNum, isLastPage ? 4UL : 3UL); + } else if (panel.expanded && pageNum < _lastPageNum && panel.activePage > 0) { + panel.activePage = MAX(panel.activePage + pageNum - _lastPageNum, pageNum == 0 ? 0UL : 1UL); + } + } + highlightedIndex += pageSize * panel.activePage; + } if (showingStatus) { [self clearBuffer]; @@ -909,7 +951,7 @@ - (void)rimeUpdate { } } else { if (_inlinePreedit && !_showingSwitcherMenu) { - if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) { + if (_inlinePlaceholder && preeditText.length == 0 && numCandidates > 0) { [self showPlaceholder:kFullWidthSpace]; } else if (!didCommit || preeditText.length > 0) { [self showPreeditString:preeditText @@ -925,20 +967,44 @@ - (void)rimeUpdate { } } // update candidates - NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate]; - NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate]; - for (NSUInteger i = 0; i < numCandidate; ++i) { + NSMutableArray *candidates = numCandidates ? [[NSMutableArray alloc] init] : nil; + NSMutableArray *comments = numCandidates ? [[NSMutableArray alloc] init] : nil; + if (numCandidates > 0 && panel.expanded && panel.activePage > 0) { + NSUInteger index = pageSize * (pageNum - panel.activePage); + RimeCandidateListIterator iterator; + if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) { + NSUInteger endIndex = pageSize * pageNum; + while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) { + [candidates addObject:@(iterator.candidate.text)]; + [comments addObject:@(iterator.candidate.comment ? : "")]; + } + rime_get_api()->candidate_list_end(&iterator); + } + } + for (NSUInteger i = 0; i < numCandidates; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; } + if (numCandidates > 0 && panel.expanded && panel.activePage < 5) { + NSUInteger index = pageSize * (pageNum + 1); + RimeCandidateListIterator iterator; + if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) { + NSUInteger endIndex = pageSize * (pageNum + 5 - panel.activePage); + while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) { + [candidates addObject:@(iterator.candidate.text)]; + [comments addObject:@(iterator.candidate.comment ? : "")]; + } + rime_get_api()->candidate_list_end(&iterator); + } + } [self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText selRange:NSMakeRange(start, end - start) caretPos:_showingSwitcherMenu ? NSNotFound : caretPos candidates:candidates comments:comments - highlightedIndex:(NSUInteger)ctx.menu.highlighted_candidate_index - pageNum:(NSUInteger)ctx.menu.page_no - lastPage:(BOOL)ctx.menu.is_last_page]; + highlightedIndex:highlightedIndex + pageNum:pageNum + lastPage:isLastPage]; rime_get_api()->free_context(&ctx); } else { [self hidePalettes]; diff --git a/SquirrelPanel.h b/SquirrelPanel.h index aed2cab76..2b79e84e8 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -14,8 +14,11 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) { // Linear candidate list layout, as opposed to stacked candidate list layout. @property(nonatomic, readonly) BOOL linear; -// Tabled candidate list layout, a subtype of linear candidate list with tabled layout. -@property(nonatomic, readonly) BOOL tabled; +// Tabular candidate list layout, initializes as tab-aligned linear layout, expandable to stack more candidates +@property(nonatomic, readonly) BOOL tabular; +@property(nonatomic, readonly) BOOL locked; +@property(nonatomic, assign) BOOL expanded; +@property(nonatomic, assign) NSUInteger activePage; // Vertical text orientation, as opposed to horizontal text orientation. @property(nonatomic, readonly) BOOL vertical; // Show preedit text inline. @@ -29,6 +32,8 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) { // position of the text input I-beam cursor on screen. @property(nonatomic, assign) NSRect IbeamRect; +- (NSUInteger)candidateIndexOnDirection:(SquirrelIndex)arrowKey; + - (void)showPreedit:(NSString *)preedit selRange:(NSRange)selRange caretPos:(NSUInteger)caretPos @@ -43,8 +48,7 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) { - (void)updateStatusLong:(NSString *)messageLong statusShort:(NSString *)messageShort; -- (void)loadConfig:(SquirrelConfig *)config - forAppearance:(SquirrelAppear)appear; +- (void)loadConfig:(SquirrelConfig *)config; - (void)loadLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index b171b905b..8c64d2af5 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -1,5 +1,6 @@ #import "SquirrelPanel.h" +#import "SquirrelApplicationDelegate.h" #import "SquirrelConfig.h" #import @@ -244,6 +245,25 @@ + (NSColorSpace *)labColorSpace { @end // NSColorSpace (labColorSpace) +@implementation NSColor (semanticColors) + ++ (NSColor *)secondaryTextColor { + if (@available(macOS 10.10, *)) { + return NSColor.secondaryLabelColor; + } else { + return NSColor.disabledControlTextColor; + } +} + ++ (NSColor *)accentColor { + if (@available(macOS 10.14, *)) { + return NSColor.controlAccentColor; + } else { + return [NSColor colorForControlTint:NSColor.currentControlTint]; + } +} + +@end @implementation NSColor (colorWithLabColorSpace) @@ -322,20 +342,19 @@ typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) { @property(nonatomic, readonly) CGFloat cornerRadius; @property(nonatomic, readonly) CGFloat highlightedCornerRadius; @property(nonatomic, readonly) CGFloat separatorWidth; -@property(nonatomic, readonly) NSSize borderInset; @property(nonatomic, readonly) CGFloat linespace; @property(nonatomic, readonly) CGFloat preeditLinespace; @property(nonatomic, readonly) CGFloat alpha; @property(nonatomic, readonly) CGFloat translucency; @property(nonatomic, readonly) CGFloat lineLength; +@property(nonatomic, readonly) NSSize borderInset; @property(nonatomic, readonly) BOOL showPaging; @property(nonatomic, readonly) BOOL rememberSize; -@property(nonatomic, readonly) BOOL tabled; +@property(nonatomic, readonly) BOOL tabular; @property(nonatomic, readonly) BOOL linear; @property(nonatomic, readonly) BOOL vertical; @property(nonatomic, readonly) BOOL inlinePreedit; @property(nonatomic, readonly) BOOL inlineCandidate; -@property(nonatomic, readonly) SquirrelStatusMessageType statusMessageType; @property(nonatomic, strong, readonly) NSDictionary *attrs; @property(nonatomic, strong, readonly) NSDictionary *highlightedAttrs; @@ -360,14 +379,17 @@ typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) { @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; @property(nonatomic, strong, readonly) NSAttributedString *symbolDeleteFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolDeleteStroke; +@property(nonatomic, strong, readonly) NSAttributedString *symbolCompress; +@property(nonatomic, strong, readonly) NSAttributedString *symbolExpand; +@property(nonatomic, strong, readonly) NSAttributedString *symbolLock; @property(nonatomic, strong, readonly) NSString *selectKeys; @property(nonatomic, strong, readonly) NSString *candidateFormat; @property(nonatomic, strong, readonly) NSArray *labels; @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; - -- (instancetype)init; +@property(nonatomic, readonly) SquirrelStatusMessageType statusMessageType; +@property(nonatomic, readonly) NSUInteger pageSize; - (void) setBackColor:(NSColor *)backColor highlightedCandidateBackColor:(NSColor *)highlightedCandidateBackColor @@ -379,15 +401,15 @@ - (void) setBackColor:(NSColor *)backColor - (void) setCornerRadius:(CGFloat)cornerRadius highlightedCornerRadius:(CGFloat)highlightedCornerRadius separatorWidth:(CGFloat)separatorWidth - borderInset:(NSSize)borderInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(CGFloat)translucency lineLength:(CGFloat)lineLength + borderInset:(NSSize)borderInset showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize - tabled:(BOOL)tabled + tabular:(BOOL)tabular linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -597,22 +619,6 @@ static CGFloat getLineHeight(NSFont *font, BOOL vertical) { return formatted; } -+ (NSColor *)secondaryTextColor { - if (@available(macOS 10.10, *)) { - return NSColor.secondaryLabelColor; - } else { - return NSColor.disabledControlTextColor; - } -} - -+ (NSColor *)accentColor { - if (@available(macOS 10.14, *)) { - return NSColor.controlAccentColor; - } else { - return [NSColor colorForControlTint:NSColor.currentControlTint]; - } -} - - (instancetype)init { if (self = [super init]) { NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; @@ -647,14 +653,14 @@ - (instancetype)init { highlightedAttrs[NSForegroundColorAttributeName] = NSColor.selectedMenuItemTextColor; NSMutableDictionary *labelAttrs = attrs.mutableCopy; - labelAttrs[NSForegroundColorAttributeName] = SquirrelTheme.accentColor; + labelAttrs[NSForegroundColorAttributeName] = NSColor.accentColor; labelAttrs[NSFontAttributeName] = userMonoFont; NSMutableDictionary *labelHighlightedAttrs = labelAttrs.mutableCopy; labelHighlightedAttrs[NSForegroundColorAttributeName] = NSColor.alternateSelectedControlTextColor; NSMutableDictionary *commentAttrs = [[NSMutableDictionary alloc] init]; - commentAttrs[NSForegroundColorAttributeName] = SquirrelTheme.secondaryTextColor; + commentAttrs[NSForegroundColorAttributeName] = NSColor.secondaryTextColor; commentAttrs[NSFontAttributeName] = userFont; NSMutableDictionary *commentHighlightedAttrs = commentAttrs.mutableCopy; @@ -719,15 +725,15 @@ - (void) setBackColor:(NSColor *)backColor - (void) setCornerRadius:(CGFloat)cornerRadius highlightedCornerRadius:(CGFloat)highlightedCornerRadius separatorWidth:(CGFloat)separatorWidth - borderInset:(NSSize)borderInset linespace:(CGFloat)linespace preeditLinespace:(CGFloat)preeditLinespace alpha:(CGFloat)alpha translucency:(CGFloat)translucency lineLength:(CGFloat)lineLength + borderInset:(NSSize)borderInset showPaging:(BOOL)showPaging rememberSize:(BOOL)rememberSize - tabled:(BOOL)tabled + tabular:(BOOL)tabular linear:(BOOL)linear vertical:(BOOL)vertical inlinePreedit:(BOOL)inlinePreedit @@ -735,15 +741,15 @@ - (void) setCornerRadius:(CGFloat)cornerRadius _cornerRadius = cornerRadius; _highlightedCornerRadius = highlightedCornerRadius; _separatorWidth = separatorWidth; - _borderInset = borderInset; _linespace = linespace; _preeditLinespace = preeditLinespace; _alpha = alpha; _translucency = translucency; _lineLength = lineLength; + _borderInset = borderInset; _showPaging = showPaging; _rememberSize = rememberSize; - _tabled = tabled; + _tabular = tabular; _linear = linear; _vertical = vertical; _inlinePreedit = inlinePreedit; @@ -777,44 +783,12 @@ - (void) setAttrs:(NSDictionary *)attrs sepAttrs[NSVerticalGlyphFormAttributeName] = @(NO); sepAttrs[NSKernAttributeName] = @(0.0); _separator = [[NSAttributedString alloc] initWithString: - _linear ? (_tabled ? [kFullWidthSpace stringByAppendingString:@"\t"] - : kFullWidthSpace) : @"\n" attributes:sepAttrs]; + _linear ? (_tabular ? [kFullWidthSpace stringByAppendingString:@"\t"] + : kFullWidthSpace) : @"\n" attributes:sepAttrs]; // Symbols for function buttons NSString *attmCharacter = [NSString stringWithFormat:@"%C", (unichar)NSAttachmentCharacter]; - NSTextAttachment *attmBackFill = [[NSTextAttachment alloc] init]; - attmBackFill.image = [NSImage imageNamed:[NSString stringWithFormat: - @"Symbols/chevron.%@.circle.fill", _linear ? @"up" : @"left"]]; - NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; - attrsBackFill[NSAttachmentAttributeName] = attmBackFill; - _symbolBackFill = [[NSAttributedString alloc] initWithString:attmCharacter - attributes:attrsBackFill]; - - NSTextAttachment *attmBackStroke = [[NSTextAttachment alloc] init]; - attmBackStroke.image = [NSImage imageNamed:[NSString stringWithFormat: - @"Symbols/chevron.%@.circle", _linear ? @"up" : @"left"]]; - NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; - attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; - _symbolBackStroke = [[NSAttributedString alloc] initWithString:attmCharacter - attributes:attrsBackStroke]; - - NSTextAttachment *attmForwardFill = [[NSTextAttachment alloc] init]; - attmForwardFill.image = [NSImage imageNamed:[NSString stringWithFormat: - @"Symbols/chevron.%@.circle.fill", _linear ? @"down" : @"right"]]; - NSMutableDictionary *attrsForwardFill = pagingAttrs.mutableCopy; - attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; - _symbolForwardFill = [[NSAttributedString alloc] initWithString:attmCharacter - attributes:attrsForwardFill]; - - NSTextAttachment *attmForwardStroke = [[NSTextAttachment alloc] init]; - attmForwardStroke.image = [NSImage imageNamed:[NSString stringWithFormat: - @"Symbols/chevron.%@.circle", _linear ? @"down" : @"right"]]; - NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; - attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; - _symbolForwardStroke = [[NSAttributedString alloc] initWithString:attmCharacter - attributes:attrsForwardStroke]; - NSTextAttachment *attmDeleteFill = [[NSTextAttachment alloc] init]; attmDeleteFill.image = [NSImage imageNamed:@"Symbols/delete.backward.fill"]; NSMutableDictionary *attrsDeleteFill = preeditAttrs.mutableCopy; @@ -830,6 +804,60 @@ - (void) setAttrs:(NSDictionary *)attrs attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @(NO); _symbolDeleteStroke = [[NSAttributedString alloc] initWithString:attmCharacter attributes:attrsDeleteStroke]; + if (_tabular) { + NSTextAttachment *attmCompress = [[NSTextAttachment alloc] init]; + attmCompress.image = [NSImage imageNamed:@"Symbols/chevron.up"]; + NSMutableDictionary *attrsCompress = pagingAttrs.mutableCopy; + attrsCompress[NSAttachmentAttributeName] = attmCompress; + _symbolCompress = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsCompress]; + + NSTextAttachment *attmExpand = [[NSTextAttachment alloc] init]; + attmExpand.image = [NSImage imageNamed:@"Symbols/chevron.down"]; + NSMutableDictionary *attrsExpand = pagingAttrs.mutableCopy; + attrsExpand[NSAttachmentAttributeName] = attmExpand; + _symbolExpand = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsExpand]; + + NSTextAttachment *attmLock = [[NSTextAttachment alloc] init]; + attmLock.image = [NSImage imageNamed:@"Symbols/lock.fill"]; + NSMutableDictionary *attrsLock = pagingAttrs.mutableCopy; + attrsLock[NSAttachmentAttributeName] = attmLock; + _symbolLock = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsLock]; + } else if (_showPaging) { + NSTextAttachment *attmBackFill = [[NSTextAttachment alloc] init]; + attmBackFill.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle.fill", _linear ? @"up" : @"left"]]; + NSMutableDictionary *attrsBackFill = pagingAttrs.mutableCopy; + attrsBackFill[NSAttachmentAttributeName] = attmBackFill; + _symbolBackFill = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsBackFill]; + + NSTextAttachment *attmBackStroke = [[NSTextAttachment alloc] init]; + attmBackStroke.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle", _linear ? @"up" : @"left"]]; + NSMutableDictionary *attrsBackStroke = pagingAttrs.mutableCopy; + attrsBackStroke[NSAttachmentAttributeName] = attmBackStroke; + _symbolBackStroke = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsBackStroke]; + + NSTextAttachment *attmForwardFill = [[NSTextAttachment alloc] init]; + attmForwardFill.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle.fill", _linear ? @"down" : @"right"]]; + NSMutableDictionary *attrsForwardFill = pagingAttrs.mutableCopy; + attrsForwardFill[NSAttachmentAttributeName] = attmForwardFill; + _symbolForwardFill = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsForwardFill]; + + NSTextAttachment *attmForwardStroke = [[NSTextAttachment alloc] init]; + attmForwardStroke.image = [NSImage imageNamed:[NSString stringWithFormat: + @"Symbols/chevron.%@.circle", _linear ? @"down" : @"right"]]; + NSMutableDictionary *attrsForwardStroke = pagingAttrs.mutableCopy; + attrsForwardStroke[NSAttachmentAttributeName] = attmForwardStroke; + _symbolForwardStroke = [[NSAttributedString alloc] initWithString:attmCharacter + attributes:attrsForwardStroke]; + } } - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle @@ -847,6 +875,7 @@ - (void)setSelectKeys:(NSString *)selectKeys directUpdate:(BOOL)update { _selectKeys = selectKeys; _labels = labels; + _pageSize = labels.count; if (update && _candidateFormat) { [self updateCandidateFormats]; } @@ -869,7 +898,7 @@ - (void)updateCandidateFormats { } NSRange candidateRange = [candidateFormat rangeOfString:@"%@"]; if (labelRange.location > candidateRange.location) { - [candidateFormat setString:kDefaultCandidateFormat]; + candidateFormat.string = kDefaultCandidateFormat; candidateRange = [candidateFormat rangeOfString:@"%@"]; } labelRange = NSMakeRange(0, candidateRange.location); @@ -1153,12 +1182,19 @@ - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutMan @interface SquirrelView : NSView +typedef struct { + NSUInteger index; + NSUInteger row; + NSUInteger tabColumn; +} SquirrelTabularPosition; + @property(nonatomic, strong, readonly) NSTextView *textView; @property(nonatomic, strong, readonly) NSTextStorage *textStorage; @property(nonatomic, strong, readonly) SquirrelTheme *currentTheme; @property(nonatomic, strong, readonly) CAShapeLayer *shape; @property(nonatomic, strong, readonly) NSMutableArray *candidatePaths; @property(nonatomic, strong, readonly) NSMutableArray *pagingPaths; +@property(nonatomic, strong, readonly) NSBezierPath *expanderPath; @property(nonatomic, strong, readonly) NSBezierPath *deleteBackPath; @property(nonatomic, strong, readonly) NSArray *candidateRanges; @property(nonatomic, readonly) NSRange preeditRange; @@ -1172,6 +1208,8 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSRect pagingBlock; @property(nonatomic, readonly) NSEdgeInsets alignmentRectInsets; @property(nonatomic, readonly) SquirrelAppear appear; +@property(nonatomic, readonly) SquirrelTabularPosition *tabularPositions; +@property(nonatomic) BOOL expanded; - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(macos(12.0)); @@ -1238,7 +1276,6 @@ - (instancetype)initWithFrame:(NSRect)frameRect { self.wantsLayer = YES; self.layer.geometryFlipped = YES; self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; - self.layerUsesCoreImageFilters = YES; if (@available(macOS 12.0, *)) { SquirrelTextLayoutManager *textLayoutManager = [[SquirrelTextLayoutManager alloc] init]; @@ -1270,7 +1307,6 @@ - (instancetype)initWithFrame:(NSRect)frameRect { textContainer:textContainer]; } _textView.drawsBackground = NO; - _textView.editable = NO; _textView.selectable = NO; _textView.wantsLayer = NO; @@ -1520,32 +1556,39 @@ - (void)drawViewWithInsets:(NSEdgeInsets)alignmentRectInsets _preeditRange = preeditRange; _highlightedPreeditRange = highlightedPreeditRange; _pagingRange = pagingRange; + _tabularPositions = candidateRanges.count > 0 ? new SquirrelTabularPosition[candidateRanges.count] : NULL; + _expanderPath = nil; _deleteBackPath = nil; _candidatePaths = [[NSMutableArray alloc] initWithCapacity:candidateRanges.count]; - _pagingPaths = [[NSMutableArray alloc] initWithCapacity:pagingRange.length > 0 ? 2 : 0]; + _pagingPaths = [[NSMutableArray alloc] initWithCapacity: + pagingRange.length > 0 && !self.currentTheme.tabular ? 2 : 0]; _functionButton = kVoidSymbol; // invalidate Rect beyond bound of textview to clear any out-of-bound drawing from last round - [self setNeedsDisplayInRect:self.bounds]; - [self.textView setNeedsDisplayInRect:self.bounds]; + self.needsDisplayInRect = self.bounds; + _textView.needsDisplayInRect = self.bounds; } - (void)highlightFunctionButton:(SquirrelIndex)functionButton { _functionButton = functionButton; + if (_expanderPath) { + self.needsDisplayInRect = _expanderPath.bounds; + _textView.needsDisplayInRect = _expanderPath.bounds; + } if (_deleteBackPath) { - [self setNeedsDisplayInRect:_deleteBackPath.bounds]; - [self.textView setNeedsDisplayInRect:_deleteBackPath.bounds]; + self.needsDisplayInRect = _deleteBackPath.bounds; + _textView.needsDisplayInRect = _deleteBackPath.bounds; } if (_pagingPaths.count > 0) { - [self setNeedsDisplayInRect:_pagingPaths[0].bounds]; - [self setNeedsDisplayInRect:_pagingPaths[1].bounds]; - [self.textView setNeedsDisplayInRect:_pagingPaths[0].bounds]; - [self.textView setNeedsDisplayInRect:_pagingPaths[1].bounds]; + self.needsDisplayInRect = _pagingPaths[0].bounds; + self.needsDisplayInRect = _pagingPaths[1].bounds; + _textView.needsDisplayInRect = _pagingPaths[0].bounds; + _textView.needsDisplayInRect = _pagingPaths[1].bounds; } } // Bezier cubic curve, which has continuous roundness static NSBezierPath * squirclePath(NSArray *vertex, CGFloat radius) { - if (vertex.count < 1) { + if (vertex.count == 0) { return nil; } NSBezierPath *path = [NSBezierPath bezierPath]; @@ -1559,9 +1602,9 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton { CGVector nextDiff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); CGVector lastDiff; if (ABS(nextDiff.dx) >= ABS(nextDiff.dy)) { - endPoint = NSMakePoint(point.x + nextDiff.dx / 2, nextPoint.y); + endPoint = NSMakePoint(point.x + nextDiff.dx * 0.5, nextPoint.y); } else { - endPoint = NSMakePoint(nextPoint.x, point.y + nextDiff.dy / 2); + endPoint = NSMakePoint(nextPoint.x, point.y + nextDiff.dy * 0.5); } [path moveToPoint:endPoint]; for (NSUInteger i = 0; i < vertex.count; ++i) { @@ -1570,14 +1613,14 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton { nextPoint = vertex[(i + 1) % vertex.count].pointValue; nextDiff = CGVectorMake(nextPoint.x - point.x, nextPoint.y - point.y); if (ABS(nextDiff.dx) >= ABS(nextDiff.dy)) { - arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dx), ABS(lastDiff.dy)) / 2); + arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dx), ABS(lastDiff.dy)) * 0.5); point.y = nextPoint.y; startPoint = NSMakePoint(point.x, point.y - copysign(arcRadius, lastDiff.dy)); controlPoint1 = NSMakePoint(point.x, point.y - copysign(arcRadius * 0.1, lastDiff.dy)); endPoint = NSMakePoint(point.x + copysign(arcRadius, nextDiff.dx), nextPoint.y); controlPoint2 = NSMakePoint(point.x + copysign(arcRadius * 0.1, nextDiff.dx), nextPoint.y); } else { - arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dy), ABS(lastDiff.dx)) / 2); + arcRadius = MIN(radius * 1.5, MIN(ABS(nextDiff.dy), ABS(lastDiff.dx)) * 0.5); point.x = nextPoint.x; startPoint = NSMakePoint(point.x - copysign(arcRadius, lastDiff.dx), point.y); controlPoint1 = NSMakePoint(point.x - copysign(arcRadius * 0.1, lastDiff.dx), point.y); @@ -1631,7 +1674,7 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton { return @[leadingVertex[0], leadingVertex[1], bodyVertex[0], trailingVertex[1], trailingVertex[2], trailingVertex[3], bodyVertex[2], leadingVertex[3]]; } else { - return @[]; + return nil; } } @@ -1662,31 +1705,37 @@ - (CAShapeLayer *)getFunctionButtonLayer { NSColor *buttonColor; NSBezierPath *buttonPath; switch (_functionButton) { - case kPageUp: + case kPageUpKey: buttonColor = hooverColor(theme.linear ? theme.highlightedCandidateBackColor : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[0]; break; - case kHome: + case kHomeKey: buttonColor = disabledColor(theme.linear ? theme.highlightedCandidateBackColor : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[0]; break; - case kPageDown: + case kPageDownKey: buttonColor = hooverColor(theme.linear ? theme.highlightedCandidateBackColor : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[1]; break; - case kEnd: + case kEndKey: buttonColor = disabledColor(theme.linear ? theme.highlightedCandidateBackColor : theme.highlightedPreeditBackColor, self.appear); buttonPath = _pagingPaths[1]; break; - case kBackSpace: + case kExpandButton: + case kCompressButton: + case kLockButton: + buttonColor = hooverColor(theme.highlightedCandidateBackColor, self.appear); + buttonPath = _expanderPath; + break; + case kBackSpaceKey: buttonColor = hooverColor(theme.highlightedPreeditBackColor, self.appear); buttonPath = _deleteBackPath; break; - case kEscape: + case kEscapeKey: buttonColor = disabledColor(theme.highlightedPreeditBackColor, self.appear); buttonPath= _deleteBackPath; break; @@ -1809,6 +1858,7 @@ - (void)updateLayer { _candidateBlock = NSZeroRect; NSBezierPath *candidateBlockPath; NSBezierPath *gridPath; + NSBezierPath *activePagePath; if (candidateBlockRange.length > 0) { _candidateBlock = [self blockRectForRange:candidateBlockRange]; _candidateBlock.size.width = backgroundRect.size.width; @@ -1830,8 +1880,9 @@ - (void)updateLayer { if (theme.linear) { CGFloat gridOriginY; CGFloat tabInterval; - CGFloat kerning = [theme.attrs[NSKernAttributeName] doubleValue]; - if (theme.tabled) { + NSUInteger rowNum = 0; + NSRect activePageBlock = NSZeroRect; + if (theme.tabular) { gridPath = [NSBezierPath bezierPath]; gridOriginY = NSMinY(_candidateBlock); tabInterval = theme.separatorWidth * 2; @@ -1876,31 +1927,42 @@ - (void)updateLayer { bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } - if (theme.tabled) { + if (theme.tabular) { + if (self.expanded && i / theme.pageSize == _highlightedIndex / theme.pageSize) { + if (i % theme.pageSize == 0) { + activePageBlock.origin = NSIsEmptyRect(leadingRect) ? bodyRect.origin : leadingRect.origin; + } else if (i % theme.pageSize == theme.pageSize - 1) { + activePageBlock.size.height = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect) - activePageBlock.origin.y; + activePageBlock.size.width = _candidateBlock.size.width - theme.symbolExpand.size.width - floor(theme.separatorWidth * 0.5); + activePagePath = squirclePath(rectVertex(activePageBlock), + MIN(theme.highlightedCornerRadius, NSHeight(activePageBlock) * 0.5)); + } + } CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); if (ABS(bottomEdge - gridOriginY) > 2 && ABS(bottomEdge - NSMaxY(_candidateBlock)) > 2) { // horizontal border - [gridPath moveToPoint:NSMakePoint(NSMinX(_candidateBlock) + - theme.separatorWidth * 0.5, bottomEdge)]; - [gridPath lineToPoint:NSMakePoint(NSMaxX(_candidateBlock) - - theme.separatorWidth * 0.5, bottomEdge)]; + [gridPath moveToPoint:NSMakePoint(NSMinX(_candidateBlock) + ceil(theme.separatorWidth * 0.5), bottomEdge)]; + [gridPath lineToPoint:NSMakePoint(NSMaxX(_candidateBlock) - theme.symbolExpand.size.width - theme.separatorWidth, bottomEdge)]; gridOriginY = bottomEdge; + ++rowNum; } CGPoint headOrigin = (NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect).origin; - if (headOrigin.x > NSMinX(_candidateBlock) + theme.separatorWidth + kerning) { // vertical bar + NSUInteger headTabColumn = (NSUInteger)floor((headOrigin.x - theme.borderInset.width) / tabInterval); + if (headOrigin.x > NSMinX(_candidateBlock) + theme.separatorWidth) { // vertical bar [gridPath moveToPoint:NSMakePoint(headOrigin.x, headOrigin.y + cornerRadius * 0.8)]; [gridPath lineToPoint:NSMakePoint(headOrigin.x, NSMaxY(NSIsEmptyRect(leadingRect) ? bodyRect : leadingRect) - cornerRadius * 0.8)]; } CGFloat tailEdge = NSMaxX(NSIsEmptyRect(trailingRect) ? bodyRect : trailingRect); - CGFloat tabPosition = ceil((tailEdge + kerning * 0.5 - theme.borderInset.width) / tabInterval) * tabInterval + theme.borderInset.width; + CGFloat tailTabPosition = ceil((tailEdge - theme.borderInset.width) / tabInterval) * tabInterval + theme.borderInset.width; if (!NSIsEmptyRect(trailingRect)) { - trailingRect.size.width += tabPosition - tailEdge; + trailingRect.size.width += tailTabPosition - tailEdge; trailingRect = [self backingAlignedRect:NSIntersectionRect(trailingRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } else if (NSIsEmptyRect(leadingRect)) { - bodyRect.size.width += tabPosition - tailEdge; + bodyRect.size.width += tailTabPosition - tailEdge; bodyRect = [self backingAlignedRect:NSIntersectionRect(bodyRect, _candidateBlock) options:NSAlignAllEdgesNearest]; } + _tabularPositions[i] = (SquirrelTabularPosition){i, rowNum, headTabColumn}; } NSBezierPath *candidatePath; @@ -1935,39 +1997,50 @@ - (void)updateLayer { // Draw paging Rect _pagingBlock = NSZeroRect; if (pagingRange.length > 0) { - NSRect pageUpRect = [self blockRectForRange: - NSMakeRange(pagingRange.location, 1)]; - NSRect pageDownRect = [self blockRectForRange: - NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; - pageDownRect.origin.x += _alignmentRectInsets.left; - pageDownRect.size.width += ceil(theme.separatorWidth * 0.5); - pageDownRect.origin.y += _alignmentRectInsets.top; - pageUpRect.origin.x += theme.borderInset.width; - pageUpRect.size.width = NSWidth(pageDownRect); // bypass the bug of getting wrong glyph position when tab is presented - pageUpRect.origin.y += _alignmentRectInsets.top; - if (theme.linear) { - pageUpRect.origin.y -= ceil(theme.linespace * 0.5); - pageUpRect.size.height += theme.linespace; - pageDownRect.origin.y -= ceil(theme.linespace * 0.5); - pageDownRect.size.height += theme.linespace; - pageUpRect = NSIntersectionRect(pageUpRect, _candidateBlock); - pageDownRect = NSIntersectionRect(pageDownRect, _candidateBlock); + if (theme.tabular) { + NSRect expanderRect = [self blockRectForRange:pagingRange]; + expanderRect.size.width += floor(theme.separatorWidth * 0.5); + expanderRect.origin.x = NSMaxX(backgroundRect) - NSWidth(expanderRect); + expanderRect.size.height += theme.linespace; + expanderRect.origin.y += _alignmentRectInsets.top - ceil(theme.linespace * 0.5); + expanderRect = [self backingAlignedRect:NSIntersectionRect(expanderRect, _candidateBlock) + options:NSAlignAllEdgesNearest]; + _expanderPath = squirclePath(rectVertex(expanderRect), + MIN(theme.highlightedCornerRadius, theme.paragraphStyle.minimumLineHeight * 0.5)); } else { - _pagingBlock = NSMakeRect(NSMinX(backgroundRect), - NSMaxY(_candidateBlock), - NSWidth(backgroundRect), - NSMaxY(backgroundRect) - NSMaxY(_candidateBlock)); - pageUpRect = NSIntersectionRect(pageUpRect, _pagingBlock); - pageDownRect = NSIntersectionRect(pageDownRect, _pagingBlock); - } - pageUpRect = [self backingAlignedRect:pageUpRect - options:NSAlignAllEdgesNearest]; - pageDownRect = [self backingAlignedRect:pageDownRect + NSRect pageUpRect = [self blockRectForRange:NSMakeRange(pagingRange.location, 1)]; + NSRect pageDownRect = [self blockRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)]; + pageDownRect.origin.x += _alignmentRectInsets.left; + pageDownRect.size.width += ceil(theme.separatorWidth * 0.5); + pageDownRect.origin.y += _alignmentRectInsets.top; + pageUpRect.origin.x += theme.borderInset.width; + // bypass the bug of getting wrong glyph position when tab is presented + pageUpRect.size.width = NSWidth(pageDownRect); + pageUpRect.origin.y += _alignmentRectInsets.top; + if (theme.linear) { + pageUpRect.origin.y -= ceil(theme.linespace * 0.5); + pageUpRect.size.height += theme.linespace; + pageDownRect.origin.y -= ceil(theme.linespace * 0.5); + pageDownRect.size.height += theme.linespace; + pageUpRect = NSIntersectionRect(pageUpRect, _candidateBlock); + pageDownRect = NSIntersectionRect(pageDownRect, _candidateBlock); + } else { + _pagingBlock = NSMakeRect(NSMinX(backgroundRect), + NSMaxY(_candidateBlock), + NSWidth(backgroundRect), + NSMaxY(backgroundRect) - NSMaxY(_candidateBlock)); + pageUpRect = NSIntersectionRect(pageUpRect, _pagingBlock); + pageDownRect = NSIntersectionRect(pageDownRect, _pagingBlock); + } + pageUpRect = [self backingAlignedRect:pageUpRect options:NSAlignAllEdgesNearest]; - CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, - MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) * 0.5); - _pagingPaths[0] = squirclePath(rectVertex(pageUpRect), cornerRadius); - _pagingPaths[1] = squirclePath(rectVertex(pageDownRect), cornerRadius); + pageDownRect = [self backingAlignedRect:pageDownRect + options:NSAlignAllEdgesNearest]; + CGFloat cornerRadius = MIN(theme.highlightedCornerRadius, + MIN(NSWidth(pageDownRect), NSHeight(pageDownRect)) * 0.5); + _pagingPaths[0] = squirclePath(rectVertex(pageUpRect), cornerRadius); + _pagingPaths[1] = squirclePath(rectVertex(pageDownRect), cornerRadius); + } } // Set layers @@ -2024,15 +2097,6 @@ - (void)updateLayer { backColorLayer.fillColor = theme.backColor.CGColor; [BackLayers addSublayer:backColorLayer]; } - // grids (in candidate block) layer - if (gridPath) { - CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; - gridLayer.path = gridPath.quartzPath; - gridLayer.lineWidth = 1.0; - gridLayer.strokeColor = [theme.commentAttrs[NSForegroundColorAttributeName] - blendedColorWithFraction:0.5 ofColor:theme.backColor].CGColor; - [BackLayers addSublayer:gridLayer]; - } // border layer CAShapeLayer *borderLayer = [[CAShapeLayer alloc] init]; borderLayer.path = borderPath.quartzPath; @@ -2055,6 +2119,15 @@ - (void)updateLayer { } // highlighted candidate layer if (_highlightedIndex < _candidatePaths.count && theme.highlightedCandidateBackColor) { + if (activePagePath) { + CAShapeLayer *activePageLayer = [[CAShapeLayer alloc] init]; + activePageLayer.path = activePagePath.quartzPath; + activePageLayer.fillColor = [[theme.highlightedCandidateBackColor + blendedColorWithFraction:0.8 + ofColor:[theme.backColor colorWithAlphaComponent:1.0]] + colorWithAlphaComponent:theme.backColor.alphaComponent].CGColor; + [BackLayers addSublayer:activePageLayer]; + } CAShapeLayer *highlightedCandidateLayer = [[CAShapeLayer alloc] init]; highlightedCandidateLayer.path = _candidatePaths[_highlightedIndex].quartzPath; highlightedCandidateLayer.fillColor = theme.highlightedCandidateBackColor.CGColor; @@ -2067,6 +2140,15 @@ - (void)updateLayer { [ForeLayers addSublayer:functionButtonLayer]; } } + // grids (in candidate block) layer + if (gridPath) { + CAShapeLayer *gridLayer = [[CAShapeLayer alloc] init]; + gridLayer.path = gridPath.quartzPath; + gridLayer.lineWidth = 1.0; + gridLayer.strokeColor = [theme.commentAttrs[NSForegroundColorAttributeName] + blendedColorWithFraction:0.5 ofColor:theme.backColor].CGColor; + [ForeLayers addSublayer:gridLayer]; + } // logo at the beginning for status message if (NSIsEmptyRect(_preeditBlock) && NSIsEmptyRect(_candidateBlock)) { CALayer *logoLayer = [[CALayer alloc] init]; @@ -2090,14 +2172,16 @@ - (NSUInteger)getIndexFromMouseSpot:(NSPoint)spot { NSPoint point = [self convertPoint:spot fromView:nil]; if (NSPointInRect(point, self.bounds)) { if (NSPointInRect(point, _preeditBlock)) { - return [_deleteBackPath containsPoint:point] ? kBackSpace : kCodeInput; + return [_deleteBackPath containsPoint:point] ? kBackSpaceKey : kCodeInputArea; } - if (_pagingPaths.count > 0) { + if ([_expanderPath containsPoint:point]) { + return kExpandButton; + } else if (_pagingPaths.count > 0) { if ([_pagingPaths[0] containsPoint:point]) { - return kPageUp; + return kPageUpKey; } if ([_pagingPaths[1] containsPoint:point]) { - return kPageDown; + return kPageDownKey; } } for (NSUInteger i = 0; i < _candidatePaths.count; ++i) { @@ -2215,7 +2299,6 @@ @implementation SquirrelPanel { NSSize _maxSize; CGFloat _textWidthLimit; - NSPoint _scrollLocus; BOOL _initPosition; NSTimer *_statusTimer; @@ -2223,8 +2306,8 @@ @implementation SquirrelPanel { NSUInteger _highlightedIndex; NSUInteger _functionButton; NSUInteger _caretPos; + NSUInteger _pageNum; BOOL _caretAtHome; - BOOL _firstPage; BOOL _lastPage; } @@ -2232,8 +2315,8 @@ - (BOOL)linear { return _view.currentTheme.linear; } -- (BOOL)tabled { - return _view.currentTheme.tabled; +- (BOOL)tabular { + return _view.currentTheme.tabular; } - (BOOL)vertical { @@ -2248,6 +2331,43 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } +- (BOOL)expanded { + return _view.expanded; +} + +- (void)setExpanded:(BOOL)expanded { + if (_view.currentTheme.tabular && !_locked) { + _view.expanded = expanded; + } +} + +- (void)setActivePage:(NSUInteger)activePage { + if (_view.currentTheme.tabular && _view.expanded) { + _activePage = MAX(MIN(activePage, 5UL), 0UL); + } +} + +- (void)setLocked:(BOOL)locked { + if (_view.currentTheme.tabular) { + _locked = locked; + SquirrelConfig *userConfig = [[SquirrelConfig alloc] init]; + if ([userConfig openUserConfig:@"user"]) { + [userConfig setBool:locked forOption:@"var/option/_lock_tabular"]; + } + [userConfig close]; + } +} + +- (BOOL)getLocked { + BOOL locked = NO; + SquirrelConfig *userConfig = [[SquirrelConfig alloc] init]; + if ([userConfig openUserConfig:@"user"]) { + locked = [userConfig getBoolForOption:@"var/option/_lock_tabular"]; + } + [userConfig close]; + return locked; +} + - (SquirrelInputController *)inputController { return SquirrelInputController.currentController; } @@ -2291,157 +2411,240 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification { } } -- (void)sendEvent:(NSEvent *)event { - NSUInteger cursorIndex; - switch (event.type) { - case NSEventTypeLeftMouseDown: - if (event.clickCount == 1 && _functionButton == kCodeInput) { - NSPoint spot = [_view.textView convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger inputIndex = [_view.textView characterIndexForInsertionAtPoint:spot]; - if (inputIndex == 0) { - [self.inputController perform:kSELECT onIndex:kHome]; - } else if (inputIndex < _caretPos) { - [self.inputController moveCursor:_caretPos - toPosition:inputIndex - inlinePreedit:NO - inlineCandidate:NO]; - } else if (inputIndex >= _view.preeditRange.length) { - [self.inputController perform:kSELECT onIndex:kEnd]; - } else if (inputIndex > _caretPos + 1) { - [self.inputController moveCursor:_caretPos - toPosition:inputIndex - 1 - inlinePreedit:NO - inlineCandidate:NO]; - } - } - break; - case NSEventTypeLeftMouseUp: - cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; - if (event.clickCount == 1 && cursorIndex != NSNotFound && - (cursorIndex == _highlightedIndex || cursorIndex == _functionButton)) { - [self.inputController perform:kSELECT onIndex:cursorIndex]; +- (NSUInteger)candidateIndexOnDirection:(SquirrelIndex)arrowKey { + if (!self.tabular || _numCandidates == 0 || _highlightedIndex == NSNotFound) { + return NSNotFound; + } + NSUInteger pageSize = _view.currentTheme.pageSize; + NSUInteger currentTabColumn = _view.tabularPositions[_highlightedIndex].tabColumn; + NSUInteger currentRow = _view.tabularPositions[_highlightedIndex].row; + if ((arrowKey == kLeftKey && self.vertical) || + (arrowKey == kDownKey && !self.vertical)) { + NSUInteger newIndex = _highlightedIndex + 1; + while (newIndex < _numCandidates && (_view.tabularPositions[newIndex].row == currentRow || + (_view.tabularPositions[newIndex].row == currentRow + 1 && + _view.tabularPositions[newIndex].tabColumn <= currentTabColumn))) { + ++newIndex; + } + if (newIndex == _numCandidates) { + return _numCandidates < pageSize * 5 ? NSNotFound : _numCandidates + pageSize * (_pageNum - _activePage); + } else { + return newIndex - 1 + pageSize * (_pageNum - _activePage); + } + } else if ((arrowKey == kRightKey && self.vertical) || + (arrowKey == kUpKey && !self.vertical)) { + NSInteger newIndex = (NSInteger)_highlightedIndex - 1; + while (newIndex >= 0 && (_view.tabularPositions[newIndex].row == currentRow || + (_view.tabularPositions[newIndex].row == currentRow - 1 && + _view.tabularPositions[newIndex].tabColumn > currentTabColumn))) { + --newIndex; + } + if (newIndex == -1) { + return _pageNum == 0 ? NSNotFound : pageSize * (_pageNum - _activePage) - 1; + } else { + return (NSUInteger)newIndex + pageSize * (_pageNum - _activePage); + } + } + return NSNotFound; +} + +// handle mouse interaction events +- (void)mouseDown:(NSEvent *)event { + if (event.clickCount > 1 || _functionButton != kCodeInputArea) { + return; + } + NSPoint spot = [_view.textView convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; + NSUInteger inputIndex = [_view.textView characterIndexForInsertionAtPoint:spot]; + if (inputIndex == 0) { + [self.inputController perform:kPROCESS onIndex:kHomeKey]; + } else if (inputIndex < _caretPos) { + [self.inputController moveCursor:_caretPos + toPosition:inputIndex + inlinePreedit:NO + inlineCandidate:NO]; + } else if (inputIndex >= _view.preeditRange.length) { + [self.inputController perform:kPROCESS onIndex:kEndKey]; + } else if (inputIndex > _caretPos + 1) { + [self.inputController moveCursor:_caretPos + toPosition:inputIndex - 1 + inlinePreedit:NO + inlineCandidate:NO]; + } +} + +- (void)mouseUp:(NSEvent *)event { + NSUInteger cursorIndex = [_view getIndexFromMouseSpot: + self.mouseLocationOutsideOfEventStream]; + if (event.clickCount > 1 || cursorIndex == NSNotFound) { + return; + } + if (cursorIndex == _highlightedIndex) { + cursorIndex += (_pageNum - _activePage) * _view.currentTheme.pageSize; + [self.inputController perform:kSELECT + onIndex:cursorIndex]; + } else if (cursorIndex == _functionButton) { + if (cursorIndex == kExpandButton) { + if (_locked) { + [self setLocked:NO]; + } else { + _view.expanded = !_view.expanded; + _activePage = 0; } - break; - case NSEventTypeRightMouseUp: - cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; - if (event.clickCount == 1 && cursorIndex != NSNotFound) { - if (cursorIndex == _highlightedIndex) { - [self.inputController perform:kDELETE onIndex:cursorIndex]; - } else if (cursorIndex == _functionButton) { - switch (cursorIndex) { - case kPageUp: - [self.inputController perform:kSELECT onIndex:kHome]; - break; - case kPageDown: - [self.inputController perform:kSELECT onIndex:kEnd]; - break; - case kBackSpace: - [self.inputController perform:kSELECT onIndex:kEscape]; - break; - } + } + [self.inputController perform:kPROCESS onIndex:cursorIndex]; + } +} + +- (void)rightMouseUp:(NSEvent *)event { + NSUInteger cursorIndex = [_view getIndexFromMouseSpot: + self.mouseLocationOutsideOfEventStream]; + if (event.clickCount > 1 || cursorIndex == NSNotFound) { + return; + } + if (cursorIndex == _highlightedIndex) { + cursorIndex += (_pageNum - _activePage) * _view.currentTheme.pageSize; + [self.inputController perform:kDELETE + onIndex:cursorIndex]; + } else if (cursorIndex == _functionButton) { + switch (cursorIndex) { + case kPageUpKey: + [self.inputController perform:kPROCESS onIndex:kHomeKey]; + break; + case kPageDownKey: + [self.inputController perform:kPROCESS onIndex:kEndKey]; + break; + case kExpandButton: + [self setLocked:!_locked]; + [self.inputController perform:kPROCESS onIndex:kLockButton]; + break; + case kBackSpaceKey: + [self.inputController perform:kPROCESS onIndex:kEscapeKey]; + break; + } + } +} + +- (void)mouseMoved:(NSEvent *)event { + if (event.modifierFlags & NSEventModifierFlagOption) { + return; + } + SquirrelTheme *theme = _view.currentTheme; + NSUInteger cursorIndex = [_view getIndexFromMouseSpot: + self.mouseLocationOutsideOfEventStream]; + if (cursorIndex != _highlightedIndex && cursorIndex != _functionButton) { + [_toolTip hide]; + } + if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlightedIndex != cursorIndex) { + _highlightedIndex = cursorIndex; + cursorIndex += (_pageNum - _activePage) * theme.pageSize; + _activePage = _highlightedIndex / theme.pageSize; + _pageNum = cursorIndex / theme.pageSize; + [_toolTip showWithToolTip:NSLocalizedString(@"candidate", nil)]; + [self.inputController perform:kHIGHLIGHT onIndex:cursorIndex]; + } else if ((cursorIndex == kPageUpKey || cursorIndex == kPageDownKey || cursorIndex == kExpandButton || + cursorIndex == kBackSpaceKey) && _functionButton != cursorIndex) { + _functionButton = cursorIndex; + switch (_functionButton) { + case kPageUpKey: + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; } - } - break; - case NSEventTypeMouseMoved: - cursorIndex = [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream]; - if (cursorIndex != _highlightedIndex && cursorIndex != _functionButton) { - [_toolTip hide]; - } - if (cursorIndex >= 0 && cursorIndex < _numCandidates && _highlightedIndex != cursorIndex) { - _highlightedIndex = cursorIndex; - [_toolTip showWithToolTip:NSLocalizedString(@"candidate", nil)]; - [self.inputController perform:kHILITE onIndex: - [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]]; - } else if ((cursorIndex == kPageUp || cursorIndex == kPageDown || - cursorIndex == kBackSpace) && _functionButton != cursorIndex) { - _functionButton = cursorIndex; - SquirrelTheme *theme = _view.currentTheme; - switch (_functionButton) { - case kPageUp: - [_view.textStorage addAttributes:theme.pagingHighlightedAttrs - range:NSMakeRange(_view.pagingRange.location, 1)]; + cursorIndex = _pageNum == 0 ? kHomeKey : kPageUpKey; + [_toolTip showWithToolTip:NSLocalizedString(_pageNum == 0 ? @"home" : @"page_up", nil)]; + break; + case kPageDownKey: + [_view.textStorage addAttributes:theme.pagingAttrs + range:NSMakeRange(_view.pagingRange.location, 1)]; + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _lastPage ? kEndKey : kPageDownKey; + [_toolTip showWithToolTip:NSLocalizedString(_lastPage ? @"end" : @"page_down", nil)]; + break; + case kExpandButton: + [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + range:_view.pagingRange]; + if (_view.preeditRange.length > 0) { + [_view.textStorage addAttributes:theme.preeditAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + } + cursorIndex = _locked ? kLockButton : _view.expanded ? kCompressButton : kExpandButton; + [_toolTip showWithToolTip:NSLocalizedString(_locked ? @"unlock" : _view.expanded ? @"compress" : @"expand", nil)]; + break; + case kBackSpaceKey: + [_view.textStorage addAttributes:theme.preeditHighlightedAttrs + range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; + if (_view.pagingRange.length > 0) { + if (theme.tabular) { [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - if (_view.preeditRange.length > 0) { - [_view.textStorage addAttributes:theme.preeditAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - } - cursorIndex = _firstPage ? kHome : kPageUp; - [_toolTip showWithToolTip:NSLocalizedString(_firstPage ? @"home" : @"page_up", nil)]; - break; - case kPageDown: + range:_view.pagingRange]; + } else { [_view.textStorage addAttributes:theme.pagingAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; - [_view.textStorage addAttributes:theme.pagingHighlightedAttrs + [_view.textStorage addAttributes:theme.pagingAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - if (_view.preeditRange.length > 0) { - [_view.textStorage addAttributes:theme.preeditAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - } - cursorIndex = _lastPage ? kEnd : kPageDown; - [_toolTip showWithToolTip:NSLocalizedString(_lastPage ? @"end" : @"page_down", nil)]; - break; - case kBackSpace: - [_view.textStorage addAttributes:theme.preeditHighlightedAttrs - range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)]; - if (_view.pagingRange.length > 0) { - [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(_view.pagingRange.location, 1)]; - [_view.textStorage addAttributes:theme.pagingAttrs - range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; - } - cursorIndex = _caretAtHome ? kEscape : kBackSpace; - [_toolTip showWithToolTip:NSLocalizedString(_caretAtHome ? @"escape" : @"delete", nil)]; - break; - } - [_view highlightFunctionButton:cursorIndex]; - [self display]; - } else if (cursorIndex == kCodeInput && _functionButton != cursorIndex) { - _functionButton = cursorIndex; - } - break; - case NSEventTypeLeftMouseDragged: - _maxSize = NSZeroSize; // reset the remember_size references after moving the panel - [self performWindowDragWithEvent:event]; - break; - case NSEventTypeScrollWheel: { - SquirrelTheme *theme = _view.currentTheme; - CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] minimumLineHeight] + - [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; - if (event.phase == NSEventPhaseBegan) { - _scrollLocus = NSZeroPoint; - } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && - !isnan(_scrollLocus.x) && !isnan(_scrollLocus.y)) { - // determine scrolling direction by confining to sectors within ±30º of any axis - if (ABS(event.scrollingDeltaX) > ABS(event.scrollingDeltaY) * sqrt(3.0)) { - _scrollLocus.x += event.scrollingDeltaX * (event.hasPreciseScrollingDeltas ? 1 : 10); - } else if (ABS(event.scrollingDeltaY) > ABS(event.scrollingDeltaX) * sqrt(3.0)) { - _scrollLocus.y += event.scrollingDeltaY * (event.hasPreciseScrollingDeltas ? 1 : 10); - } - // compare accumulated locus length against threshold and limit paging to max once - if (_scrollLocus.x > scrollThreshold) { - [self.inputController perform:kSELECT - onIndex:(theme.vertical ? kPageDown : kPageUp)]; - _scrollLocus = NSMakePoint(NAN, NAN); - } else if (_scrollLocus.y > scrollThreshold) { - [self.inputController perform:kSELECT - onIndex:kPageUp]; - _scrollLocus = NSMakePoint(NAN, NAN); - } else if (_scrollLocus.x < -scrollThreshold) { - [self.inputController perform:kSELECT - onIndex:(theme.vertical ? kPageUp : kPageDown)]; - _scrollLocus = NSMakePoint(NAN, NAN); - } else if (_scrollLocus.y < -scrollThreshold) { - [self.inputController perform:kSELECT - onIndex:kPageDown]; - _scrollLocus = NSMakePoint(NAN, NAN); + } } - } - } break; - default: - [super sendEvent:event]; - break; + cursorIndex = _caretAtHome ? kEscapeKey : kBackSpaceKey; + [_toolTip showWithToolTip:NSLocalizedString(_caretAtHome ? @"escape" : @"delete", nil)]; + break; + } + [_view highlightFunctionButton:cursorIndex]; + [self display]; + } else if (cursorIndex == kCodeInputArea && _functionButton != cursorIndex) { + _functionButton = cursorIndex; + } +} + +- (void)mouseDragged:(NSEvent *)event { + // reset the remember_size references after moving the panel + _maxSize = NSZeroSize; + [self performWindowDragWithEvent:event]; +} + +- (void)scrollWheel:(NSEvent *)event { + SquirrelTheme *theme = _view.currentTheme; + CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] minimumLineHeight] + + [theme.attrs[NSParagraphStyleAttributeName] lineSpacing]; + + static NSPoint scrollLocus = NSZeroPoint; + if (event.phase == NSEventPhaseBegan) { + scrollLocus = NSZeroPoint; + } else if ((event.phase == NSEventPhaseNone || event.momentumPhase == NSEventPhaseNone) && + !isnan(scrollLocus.x) && !isnan(scrollLocus.y)) { + // determine scrolling direction by confining to sectors within ±30º of any axis + if (ABS(event.scrollingDeltaX) > ABS(event.scrollingDeltaY) * sqrt(3.0)) { + scrollLocus.x += event.scrollingDeltaX * (event.hasPreciseScrollingDeltas ? 1 : 10); + } else if (ABS(event.scrollingDeltaY) > ABS(event.scrollingDeltaX) * sqrt(3.0)) { + scrollLocus.y += event.scrollingDeltaY * (event.hasPreciseScrollingDeltas ? 1 : 10); + } + // compare accumulated locus length against threshold and limit paging to max once + if (scrollLocus.x > scrollThreshold) { + [self.inputController perform:kPROCESS + onIndex:(theme.vertical ? kPageDownKey : kPageUpKey)]; + scrollLocus = NSMakePoint(NAN, NAN); + } else if (scrollLocus.y > scrollThreshold) { + [self.inputController perform:kPROCESS + onIndex:kPageUpKey]; + scrollLocus = NSMakePoint(NAN, NAN); + } else if (scrollLocus.x < -scrollThreshold) { + [self.inputController perform:kPROCESS + onIndex:(theme.vertical ? kPageUpKey : kPageDownKey)]; + scrollLocus = NSMakePoint(NAN, NAN); + } else if (scrollLocus.y < -scrollThreshold) { + [self.inputController perform:kPROCESS + onIndex:kPageDownKey]; + scrollLocus = NSMakePoint(NAN, NAN); + } } } @@ -2474,9 +2677,10 @@ - (void)updateDisplayParameters { if (theme.lineLength > 0) { _textWidthLimit = MIN(theme.lineLength, _textWidthLimit); } - if (theme.tabled) { - CGFloat tabInterval = theme.separatorWidth * 2; - _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; + if (theme.tabular) { + CGFloat doubleTabInterval = theme.separatorWidth * 4; + CGFloat expanderWidth = theme.symbolExpand.size.width - ceil(theme.separatorWidth * 0.5); + _textWidthLimit = floor((_textWidthLimit - expanderWidth) / doubleTabInterval) * doubleTabInterval + expanderWidth; } CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.8 - theme.borderInset.height * 2 - (theme.inlinePreedit ? ceil(theme.linespace * 0.5) : 0.0) - @@ -2648,19 +2852,19 @@ - (void)show { // rotate the view, the core in vertical mode! self.contentView.boundsRotation = theme.vertical ? -90.0 : 0.0; - [self.contentView setBoundsOrigin:theme.vertical ? NSMakePoint(0.0, NSWidth(windowRect)) : NSZeroPoint]; + self.contentView.boundsOrigin = theme.vertical ? NSMakePoint(0.0, NSWidth(windowRect)) : NSZeroPoint; NSRect viewRect = self.contentView.bounds; - [_view setBoundsOrigin:NSZeroPoint]; + _view.boundsOrigin = NSZeroPoint; _view.frame = viewRect; _view.textView.boundsRotation = 0.0; - [_view.textView setBoundsOrigin:NSZeroPoint]; + _view.textView.boundsOrigin = NSZeroPoint; _view.textView.frame = NSOffsetRect(viewRect, insets.left - _view.textView.textContainerOrigin.x, insets.top - _view.textView.textContainerOrigin.y); if (theme.translucency > 0) { - [_back setBoundsOrigin:NSZeroPoint]; + _back.boundsOrigin = NSZeroPoint; _back.frame = viewRect; _back.hidden = NO; } else { @@ -2683,10 +2887,16 @@ - (void)hide { [self orderOut:nil]; _maxSize = NSZeroSize; _initPosition = YES; + _view.expanded = NO; + _activePage = 0; } - (BOOL)shouldBreakLineInsideRange:(NSRange)range { [_view.textStorage fixFontAttributeInRange:range]; + CGFloat maxTextWidth = _textWidthLimit; + if (self.tabular) { + maxTextWidth -= _view.currentTheme.symbolExpand.size.width + floor(_view.currentTheme.separatorWidth * 0.5); + } NSUInteger __block lineCount = 0; if (@available(macOS 12.0, *)) { NSTextRange *textRange = [_view getTextRangeFromCharRange:range]; @@ -2696,7 +2906,7 @@ - (BOOL)shouldBreakLineInsideRange:(NSRange)range { options:NSTextLayoutManagerSegmentOptionsRangeNotRequired usingBlock:^BOOL(NSTextRange *segRange, CGRect segFrame, CGFloat baseline, NSTextContainer *textContainer) { - lineCount += 1 + (NSMaxX(segFrame) > _textWidthLimit); + lineCount += 1 + (NSMaxX(segFrame) > maxTextWidth); return YES; }]; } else { @@ -2707,7 +2917,7 @@ - (BOOL)shouldBreakLineInsideRange:(NSRange)range { enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange lineRange, BOOL *stop) { - lineCount += 1 + (NSMaxX(usedRect) > self->_textWidthLimit); + lineCount += 1 + (NSMaxX(usedRect) > maxTextWidth); }]; } return lineCount > 1; @@ -2780,9 +2990,10 @@ - (NSMutableAttributedString *)getPageNumString:(NSUInteger)pageNum { NSTextAttachment *pageNumAttm = [[NSTextAttachment alloc] init]; pageNumAttm.image = pageNumImage; pageNumAttm.bounds = NSMakeRect(0, font.ascender * 0.5 + font.descender * 0.5 - lineHeight * 0.5, lineHeight, lineHeight); - NSMutableAttributedString *attmString = [[NSMutableAttributedString alloc] - initWithString:[NSString stringWithFormat:@" %C ", (unichar)NSAttachmentCharacter] - attributes:pageNumAttrs]; + NSMutableAttributedString *attmString = + [[NSMutableAttributedString alloc] + initWithString:[NSString stringWithFormat:@" %C ", (unichar)NSAttachmentCharacter] + attributes:pageNumAttrs]; [attmString addAttribute:NSAttachmentAttributeName value:pageNumAttm range:NSMakeRange(1, 1)]; @@ -2803,10 +3014,10 @@ - (void)showPreedit:(NSString *)preedit [self updateDisplayParameters]; } _numCandidates = candidates.count; - _highlightedIndex = _numCandidates == 0 ? NSNotFound : highlightedIndex; + _highlightedIndex = highlightedIndex; _caretPos = caretPos; _caretAtHome = caretPos == NSNotFound || (caretPos == selRange.location && selRange.location == 1); - _firstPage = pageNum == 0; + _pageNum = pageNum; _lastPage = lastPage; _functionButton = kVoidSymbol; if (_numCandidates > 0 || preedit.length > 0) { @@ -2832,7 +3043,7 @@ - (void)showPreedit:(NSString *)preedit } NSTextStorage *text = _view.textStorage; - [text setAttributedString:[[NSMutableAttributedString alloc] init]]; + text.attributedString = [[NSAttributedString alloc] init]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); NSMutableArray *candidateRanges = [[NSMutableArray alloc] @@ -2840,10 +3051,6 @@ - (void)showPreedit:(NSString *)preedit NSRange pagingRange = NSMakeRange(NSNotFound, 0); NSUInteger candidateBlockStart; NSUInteger lineStart; - - CGFloat topMargin = preedit ? 0.0 : ceil(theme.linespace * 0.5); - CGFloat bottomMargin = _numCandidates > 0 && (theme.linear || !theme.showPaging) ? - floor(theme.linespace * 0.5) : 0.0; NSMutableParagraphStyle *paragraphStyleCandidate; CGFloat tabInterval = theme.separatorWidth * 2; CGFloat maxLineLength = 0.0; @@ -2887,22 +3094,9 @@ - (void)showPreedit:(NSString *)preedit initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { - if ([self shouldUseTabInRange:NSMakeRange(preeditLine.length - 2, 2) - maxLineLength:&maxLineLength]) { - if (theme.tabled) { - maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; - } - [text replaceCharactersInRange:NSMakeRange(preeditLine.length - 2, 1) - withString:@"\t"]; - NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; - paragraphStylePreedit.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength - options:@{}]]; - [text addAttribute:NSParagraphStyleAttributeName - value:paragraphStylePreedit - range:preeditRange]; - } - goto drawPanel; + _view.expanded = NO; + _activePage = 0; + goto alignDelete; } } @@ -2910,26 +3104,32 @@ - (void)showPreedit:(NSString *)preedit candidateBlockStart = text.length; lineStart = text.length; if (theme.linear) { - paragraphStyleCandidate = [theme.paragraphStyle copy]; + paragraphStyleCandidate = theme.paragraphStyle.copy; } for (NSUInteger idx = 0; idx < _numCandidates; ++idx) { + NSUInteger col = idx % theme.pageSize; // attributed labels are already included in candidateFormats - NSMutableAttributedString *item = (idx == highlightedIndex) ? [theme.candidateHighlightedFormats[idx] mutableCopy] - : [theme.candidateFormats[idx] mutableCopy]; - NSRange candidateRange = [item.string rangeOfString:@"%@"]; + NSMutableAttributedString *item = idx == highlightedIndex ? theme.candidateHighlightedFormats[col].mutableCopy + : theme.candidateFormats[col].mutableCopy; + NSRange candidateField = [item.string rangeOfString:@"%@"]; // get the label size for indent - CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange: - NSMakeRange(0, candidateRange.location)].size.width); + NSRange labelRange = NSMakeRange(0, candidateField.location); + CGFloat labelWidth = theme.linear ? 0.0 : ceil([item attributedSubstringFromRange:labelRange].size.width); + // hide labels in non-highlighted pages (no selection keys) + if (idx / theme.pageSize != _activePage) { + [item addAttribute:NSForegroundColorAttributeName + value:NSColor.clearColor range:labelRange]; + } // plug in candidate texts and comments into the template - [item replaceCharactersInRange:candidateRange + [item replaceCharactersInRange:candidateField withString:candidates[idx]]; - NSRange commentRange = [item.string rangeOfString:kTipSpecifier]; - if (comments[idx].length != 0) { - [item replaceCharactersInRange:commentRange + NSRange commentField = [item.string rangeOfString:kTipSpecifier]; + if (comments[idx].length > 0) { + [item replaceCharactersInRange:commentField withString:[@" " stringByAppendingString:comments[idx]]]; } else { - [item deleteCharactersInRange:commentRange]; + [item deleteCharactersInRange:commentField]; } [item formatMarkDown]; @@ -2948,10 +3148,8 @@ - (void)showPreedit:(NSString *)preedit style.paragraphSpacingBefore = annotationHeight; [text addAttribute:NSParagraphStyleAttributeName value:style range:range]; }]; - topMargin = preedit ? 0.0 : annotationHeight; - bottomMargin = (theme.linear || !theme.showPaging) ? annotationHeight : 0.0; } - if (comments[idx].length != 0 && [item.string hasSuffix:@" "]) { + if (comments[idx].length > 0 && [item.string hasSuffix:@" "]) { [item deleteCharactersInRange:NSMakeRange(item.length - 1, 1)]; } if (!theme.linear) { @@ -2959,16 +3157,17 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate.headIndent = labelWidth; } [item addAttribute:NSParagraphStyleAttributeName - value:paragraphStyleCandidate range:NSMakeRange(0, item.length)]; + value:paragraphStyleCandidate + range:NSMakeRange(0, item.length)]; // determine if the line is too wide and line break is needed, based on screen size. if (lineStart != text.length) { NSUInteger separatorStart = text.length; - // separator: linear = " "; tabled = " \t"; stacked = "\n" + // separator: linear = " "; tabular = " \t"; stacked = "\n" NSMutableAttributedString *separator = theme.separator.mutableCopy; [text appendAttributedString:separator]; [text appendAttributedString:item]; - if (theme.linear && (ceil(item.size.width) > _textWidthLimit || + if (theme.linear && (col == 0 || ceil(item.size.width) > _textWidthLimit || [self shouldBreakLineInsideRange:NSMakeRange(lineStart, text.length - lineStart)])) { [text replaceCharactersInRange:NSMakeRange(separatorStart, separator.length) withString:@"\n"]; @@ -2979,7 +3178,7 @@ - (void)showPreedit:(NSString *)preedit } // for linear layout, middle-truncate candidates that are longer than one line if (theme.linear && ceil(item.size.width) > _textWidthLimit) { - if (idx < _numCandidates - 1 || theme.showPaging) { + if (idx < _numCandidates - 1 || theme.showPaging || theme.tabular) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; } @@ -2998,10 +3197,37 @@ - (void)showPreedit:(NSString *)preedit } // paging indication - if (theme.showPaging) { + if (theme.tabular) { + [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\t" + attributes:theme.commentAttrs]]; + NSUInteger pagingStart = text.length; + NSAttributedString *expander = _locked ? theme.symbolLock : _view.expanded ? theme.symbolCompress : theme.symbolExpand; + [text appendAttributedString:expander]; + paragraphStyleCandidate = theme.paragraphStyle.mutableCopy; + [self shouldUseTabInRange:NSMakeRange(pagingStart, 1) maxLineLength:&maxLineLength]; + paragraphStyleCandidate.tabStops = @[]; + CGFloat candidateEndPosition = NSMaxX([_view blockRectForRange: + NSMakeRange(lineStart, pagingStart - 1 - lineStart)]); + for (NSUInteger i = 1; i * tabInterval < candidateEndPosition; ++i) { + [paragraphStyleCandidate addTabStop: + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft + location:i * tabInterval + options:@{}]]; + } + CGFloat expanderPosition = floor((maxLineLength - theme.symbolExpand.size.width + ceil(theme.separatorWidth * 0.5)) / + (tabInterval * 2)) * tabInterval * 2 - ceil(theme.separatorWidth * 0.5); + [paragraphStyleCandidate addTabStop: + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft + location:expanderPosition + options:@{}]]; + pagingRange = NSMakeRange(text.length - 1, 1); + [text addAttribute:NSParagraphStyleAttributeName + value:paragraphStyleCandidate + range:NSMakeRange(lineStart, text.length - lineStart)]; + + } else if (theme.showPaging) { NSMutableAttributedString *paging = [self getPageNumString:pageNum]; - [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke - atIndex:0]; + [paging insertAttributedString:pageNum > 0 ? theme.symbolBackFill : theme.symbolBackStroke atIndex:0]; [paging appendAttributedString:lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill]; [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; @@ -3013,30 +3239,15 @@ - (void)showPreedit:(NSString *)preedit lineStart = pagingStart; pagingStart += 1; } - if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) - maxLineLength:&maxLineLength] || - lineStart != candidateBlockStart) { - if (theme.tabled) { - maxLineLength = ceil((maxLineLength + theme.separatorWidth) / tabInterval) * tabInterval - theme.separatorWidth; - } else { - [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) - withString:@"\t"]; - } + if ([self shouldUseTabInRange:NSMakeRange(pagingStart, paging.length) + maxLineLength:&maxLineLength] || lineStart != candidateBlockStart) { + [text replaceCharactersInRange:NSMakeRange(pagingStart - 1, 1) + withString:@"\t"]; paragraphStyleCandidate = theme.paragraphStyle.mutableCopy; - paragraphStyleCandidate.tabStops = @[]; - NSUInteger candidateEnd = pagingStart - 1; - CGFloat candidateEndPosition = NSMaxX([_view blockRectForRange: - NSMakeRange(lineStart, candidateEnd - lineStart)]); - for (NSUInteger i = 1; i * tabInterval < candidateEndPosition; ++i) { - [paragraphStyleCandidate addTabStop: - [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft - location:i * tabInterval - options:@{}]]; - } - [paragraphStyleCandidate addTabStop: - [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength - options:@{}]]; + paragraphStyleCandidate.tabStops = + @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength + options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStyleCandidate @@ -3049,12 +3260,13 @@ - (void)showPreedit:(NSString *)preedit withString:@"\t"]; [text replaceCharactersInRange:NSMakeRange(pagingStart + paging.length - 2, 1) withString:@"\t"]; - paragraphStylePaging.tabStops = @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter - location:maxLineLength / 2 - options:@{}], - [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight - location:maxLineLength - options:@{}]]; + paragraphStylePaging.tabStops = + @[[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter + location:maxLineLength * 0.5 + options:@{}], + [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentRight + location:maxLineLength + options:@{}]]; } [text addAttribute:NSParagraphStyleAttributeName value:paragraphStylePaging @@ -3062,9 +3274,15 @@ - (void)showPreedit:(NSString *)preedit } pagingRange = NSMakeRange(text.length - paging.length, paging.length); } + +alignDelete: // right-align the backward delete symbol if (preedit && [self shouldUseTabInRange:NSMakeRange(preeditRange.length - 2, 2) maxLineLength:&maxLineLength]) { + if (theme.tabular && _numCandidates == 0) { + CGFloat expanderWidth = theme.symbolExpand.size.width - ceil(theme.separatorWidth * 0.5); + maxLineLength = floor((maxLineLength - expanderWidth) / tabInterval) * tabInterval + expanderWidth; + } [text replaceCharactersInRange:NSMakeRange(preeditRange.length - 2, 1) withString:@"\t"]; NSMutableParagraphStyle *paragraphStylePreedit = theme.preeditParagraphStyle.mutableCopy; @@ -3076,8 +3294,9 @@ - (void)showPreedit:(NSString *)preedit } // text done! -drawPanel: [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; + CGFloat topMargin = preedit ? 0.0 : ceil(theme.linespace * 0.5); + CGFloat bottomMargin = _numCandidates > 0 && (theme.linear || !theme.showPaging) ? floor(theme.linespace * 0.5) : 0.0; NSEdgeInsets insets = NSEdgeInsetsMake(theme.borderInset.height + topMargin, theme.borderInset.width + ceil(theme.separatorWidth * 0.5), theme.borderInset.height + bottomMargin, @@ -3117,9 +3336,9 @@ - (void)showStatus:(NSString *)message { _view.textView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical; NSTextStorage *text = _view.textStorage; - [text setAttributedString:[[NSAttributedString alloc] - initWithString:[NSString stringWithFormat:@"%@ %@", kFullWidthSpace, message] - attributes:theme.statusAttrs]]; + text.attributedString = [[NSAttributedString alloc] + initWithString:[NSString stringWithFormat:@"%@ %@", kFullWidthSpace, message] + attributes:theme.statusAttrs]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; NSEdgeInsets insets = NSEdgeInsetsMake(theme.borderInset.height, @@ -3137,7 +3356,7 @@ - (void)showStatus:(NSString *)message { } self.animationBehavior = NSWindowAnimationBehaviorUtilityWindow; [_view drawViewWithInsets:insets - candidateRanges:@[] + candidateRanges:nil highlightedIndex:NSNotFound preeditRange:NSMakeRange(NSNotFound, 0) highlightedPreeditRange:NSMakeRange(NSNotFound, 0) @@ -3154,40 +3373,40 @@ - (void)hideStatus:(NSTimer *)timer { [self hide]; } -static void updateCandidateListLayout(BOOL *isLinear, BOOL *isTabled, +static void updateCandidateListLayout(BOOL *isLinear, BOOL *isTabular, SquirrelConfig *config, NSString *prefix) { - NSString *candidateListLayout = [config getString: + NSString *candidateListLayout = [config getStringForOption: [prefix stringByAppendingString:@"/candidate_list_layout"]]; if ([candidateListLayout isEqualToString:@"stacked"]) { *isLinear = NO; - *isTabled = NO; + *isTabular = NO; } else if ([candidateListLayout isEqualToString:@"linear"]) { *isLinear = YES; - *isTabled = NO; - } else if ([candidateListLayout isEqualToString:@"tabled"]) { - // `tabled` is a derived layout of `linear`; tabled implies linear + *isTabular = NO; + } else if ([candidateListLayout isEqualToString:@"tabular"]) { + // `tabular` is a derived layout of `linear`; tabular implies linear *isLinear = YES; - *isTabled = YES; + *isTabular = YES; } else { // Deprecated. Not to be confused with text_orientation: horizontal - NSNumber *horizontal = [config getOptionalBool: + NSNumber *horizontal = [config getOptionalBoolForOption: [prefix stringByAppendingString:@"/horizontal"]]; if (horizontal) { *isLinear = horizontal.boolValue; - *isTabled = NO; + *isTabular = NO; } } } static void updateTextOrientation(BOOL *isVertical, SquirrelConfig *config, NSString *prefix) { - NSString *textOrientation = [config getString: + NSString *textOrientation = [config getStringForOption: [prefix stringByAppendingString:@"/text_orientation"]]; if ([textOrientation isEqualToString:@"horizontal"]) { *isVertical = NO; } else if ([textOrientation isEqualToString:@"vertical"]) { *isVertical = YES; } else { - NSNumber *vertical = [config getOptionalBool: + NSNumber *vertical = [config getOptionalBoolForOption: [prefix stringByAppendingString:@"/vertical"]]; if (vertical) { *isVertical = vertical.boolValue; @@ -3218,10 +3437,10 @@ - (void)loadLabelConfig:(SquirrelConfig *)config + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config directUpdate:(BOOL)update { - NSUInteger menuSize = (NSUInteger)[config getInt:@"menu/page_size"] ? : 5; + NSUInteger menuSize = (NSUInteger)[config getIntForOption:@"menu/page_size"] ? : 5; NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; - NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; - NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; + NSString *selectKeys = [config getStringForOption:@"menu/alternative_select_keys"]; + NSArray *selectLabels = [config getListForOption:@"menu/alternative_select_labels"]; if (selectLabels.count > 0) { for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = selectLabels[i]; @@ -3250,14 +3469,23 @@ + (void)updateTheme:(SquirrelTheme *)theme directUpdate:update]; } -- (void)loadConfig:(SquirrelConfig *)config - forAppearance:(SquirrelAppear)appear { - SquirrelTheme *theme = [_view selectTheme:appear]; +- (void)loadConfig:(SquirrelConfig *)config { NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; - [SquirrelPanel updateTheme:theme + SquirrelTheme *defaultTheme = [_view selectTheme:defaultAppear]; + [SquirrelPanel updateTheme:defaultTheme withConfig:config styleOptions:styleOptions - forAppearance:appear]; + forAppearance:defaultAppear]; + if (@available(macOS 10.14, *)) { + SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; + [SquirrelPanel updateTheme:darkTheme + withConfig:config + styleOptions:styleOptions + forAppearance:darkAppear]; + } + if (_view.currentTheme.tabular) { + _locked = [self getLocked]; + } [self updateDisplayParameters]; } @@ -3273,44 +3501,44 @@ + (void)updateTheme:(SquirrelTheme *)theme forAppearance:(SquirrelAppear)appear { // INTERFACE BOOL linear = NO; - BOOL tabled = NO; + BOOL tabular = NO; BOOL vertical = NO; - updateCandidateListLayout(&linear, &tabled, config, @"style"); + updateCandidateListLayout(&linear, &tabular, config, @"style"); updateTextOrientation(&vertical, config, @"style"); - NSNumber *inlinePreedit = [config getOptionalBool:@"style/inline_preedit"]; - NSNumber *inlineCandidate = [config getOptionalBool:@"style/inline_candidate"]; - NSNumber *showPaging = [config getOptionalBool:@"style/show_paging"]; - NSNumber *rememberSize = [config getOptionalBool:@"style/remember_size"]; - NSString *statusMessageType = [config getString:@"style/status_message_type"]; - NSString *candidateFormat = [config getString:@"style/candidate_format"]; + NSNumber *inlinePreedit = [config getOptionalBoolForOption:@"style/inline_preedit"]; + NSNumber *inlineCandidate = [config getOptionalBoolForOption:@"style/inline_candidate"]; + NSNumber *showPaging = [config getOptionalBoolForOption:@"style/show_paging"]; + NSNumber *rememberSize = [config getOptionalBoolForOption:@"style/remember_size"]; + NSString *statusMessageType = [config getStringForOption:@"style/status_message_type"]; + NSString *candidateFormat = [config getStringForOption:@"style/candidate_format"]; // TYPOGRAPHY - NSString *fontName = [config getString:@"style/font_face"]; - NSNumber *fontSize = [config getOptionalDouble:@"style/font_point" - applyConstraint:pos_round]; - NSString *labelFontName = [config getString:@"style/label_font_face"]; - NSNumber *labelFontSize = [config getOptionalDouble:@"style/label_font_point" - applyConstraint:pos_round]; - NSString *commentFontName = [config getString:@"style/comment_font_face"]; - NSNumber *commentFontSize = [config getOptionalDouble:@"style/comment_font_point" - applyConstraint:pos_round]; - NSNumber *alpha = [config getOptionalDouble:@"style/alpha" - applyConstraint:clamp_uni]; - NSNumber *translucency = [config getOptionalDouble:@"style/translucency" - applyConstraint:clamp_uni]; - NSNumber *cornerRadius = [config getOptionalDouble:@"style/corner_radius" - applyConstraint:positive]; - NSNumber *highlightedCornerRadius = [config getOptionalDouble:@"style/hilited_corner_radius" - applyConstraint:positive]; - NSNumber *borderHeight = [config getOptionalDouble:@"style/border_height" - applyConstraint:pos_ceil]; - NSNumber *borderWidth = [config getOptionalDouble:@"style/border_width" - applyConstraint:pos_ceil]; - NSNumber *lineSpacing = [config getOptionalDouble:@"style/line_spacing" - applyConstraint:pos_round]; - NSNumber *spacing = [config getOptionalDouble:@"style/spacing" - applyConstraint:pos_round]; - NSNumber *baseOffset = [config getOptionalDouble:@"style/base_offset"]; - NSNumber *lineLength = [config getOptionalDouble:@"style/line_length"]; + NSString *fontName = [config getStringForOption:@"style/font_face"]; + NSNumber *fontSize = [config getOptionalDoubleForOption:@"style/font_point" + applyConstraint:pos_round]; + NSString *labelFontName = [config getStringForOption:@"style/label_font_face"]; + NSNumber *labelFontSize = [config getOptionalDoubleForOption:@"style/label_font_point" + applyConstraint:pos_round]; + NSString *commentFontName = [config getStringForOption:@"style/comment_font_face"]; + NSNumber *commentFontSize = [config getOptionalDoubleForOption:@"style/comment_font_point" + applyConstraint:pos_round]; + NSNumber *alpha = [config getOptionalDoubleForOption:@"style/alpha" + applyConstraint:clamp_uni]; + NSNumber *translucency = [config getOptionalDoubleForOption:@"style/translucency" + applyConstraint:clamp_uni]; + NSNumber *cornerRadius = [config getOptionalDoubleForOption:@"style/corner_radius" + applyConstraint:positive]; + NSNumber *highlightedCornerRadius = [config getOptionalDoubleForOption:@"style/hilited_corner_radius" + applyConstraint:positive]; + NSNumber *borderHeight = [config getOptionalDoubleForOption:@"style/border_height" + applyConstraint:pos_ceil]; + NSNumber *borderWidth = [config getOptionalDoubleForOption:@"style/border_width" + applyConstraint:pos_ceil]; + NSNumber *lineSpacing = [config getOptionalDoubleForOption:@"style/line_spacing" + applyConstraint:pos_round]; + NSNumber *spacing = [config getOptionalDoubleForOption:@"style/spacing" + applyConstraint:pos_round]; + NSNumber *baseOffset = [config getOptionalDoubleForOption:@"style/base_offset"]; + NSNumber *lineLength = [config getOptionalDoubleForOption:@"style/line_length"]; // CHROMATICS NSColor *backColor; NSColor *borderColor; @@ -3330,17 +3558,17 @@ + (void)updateTheme:(SquirrelTheme *)theme NSString *colorScheme; if (appear == darkAppear) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString:[NSString stringWithFormat: - @"style/%@/color_scheme_dark", option]])) break; + if ((colorScheme = [config getStringForOption:[NSString stringWithFormat: + @"style/%@/color_scheme_dark", option]])) break; } - colorScheme = colorScheme ? : [config getString:@"style/color_scheme_dark"]; + colorScheme = colorScheme ? : [config getStringForOption:@"style/color_scheme_dark"]; } if (!colorScheme) { for (NSString *option in styleOptions) { - if ((colorScheme = [config getString:[NSString stringWithFormat: - @"style/%@/color_scheme", option]])) break; + if ((colorScheme = [config getStringForOption:[NSString stringWithFormat: + @"style/%@/color_scheme", option]])) break; } - colorScheme = colorScheme ? : [config getString:@"style/color_scheme"]; + colorScheme = colorScheme ? : [config getStringForOption:@"style/color_scheme"]; } BOOL isNative = !colorScheme || [colorScheme isEqualToString:@"native"]; NSArray *configPrefixes = isNative @@ -3351,66 +3579,66 @@ + (void)updateTheme:(SquirrelTheme *)theme // get color scheme and then check possible overrides from styleSwitcher for (NSString *prefix in configPrefixes) { // CHROMATICS override - config.colorSpace = [config getString:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; - backColor = [config getColor:[prefix stringByAppendingString:@"/back_color"]] ? : backColor; - borderColor = [config getColor:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; - preeditBackColor = [config getColor:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackColor; - textColor = [config getColor:[prefix stringByAppendingString:@"/text_color"]] ? : textColor; - candidateTextColor = [config getColor:[prefix stringByAppendingString:@"/candidate_text_color"]] ? : candidateTextColor; - commentTextColor = [config getColor:[prefix stringByAppendingString:@"/comment_text_color"]] ? : commentTextColor; - candidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_color"]] ? : candidateLabelColor; - highlightedBackColor = [config getColor:[prefix stringByAppendingString:@"/hilited_back_color"]] ? : highlightedBackColor; - highlightedTextColor = [config getColor:[prefix stringByAppendingString:@"/hilited_text_color"]] ? : highlightedTextColor; - highlightedCandidateBackColor = [config getColor:[prefix stringByAppendingString: - @"/hilited_candidate_back_color"]] ? : highlightedCandidateBackColor; - highlightedCandidateTextColor = [config getColor:[prefix stringByAppendingString: - @"/hilited_candidate_text_color"]] ? : highlightedCandidateTextColor; - highlightedCommentTextColor = [config getColor:[prefix stringByAppendingString: - @"/hilited_comment_text_color"]] ? : highlightedCommentTextColor; + config.colorSpace = [config getStringForOption:[prefix stringByAppendingString:@"/color_space"]] ? : config.colorSpace; + backColor = [config getColorForOption:[prefix stringByAppendingString:@"/back_color"]] ? : backColor; + borderColor = [config getColorForOption:[prefix stringByAppendingString:@"/border_color"]] ? : borderColor; + preeditBackColor = [config getColorForOption:[prefix stringByAppendingString:@"/preedit_back_color"]] ? : preeditBackColor; + textColor = [config getColorForOption:[prefix stringByAppendingString:@"/text_color"]] ? : textColor; + candidateTextColor = [config getColorForOption:[prefix stringByAppendingString:@"/candidate_text_color"]] ? : candidateTextColor; + commentTextColor = [config getColorForOption:[prefix stringByAppendingString:@"/comment_text_color"]] ? : commentTextColor; + candidateLabelColor = [config getColorForOption:[prefix stringByAppendingString:@"/label_color"]] ? : candidateLabelColor; + highlightedBackColor = [config getColorForOption:[prefix stringByAppendingString:@"/hilited_back_color"]] ? : highlightedBackColor; + highlightedTextColor = [config getColorForOption:[prefix stringByAppendingString:@"/hilited_text_color"]] ? : highlightedTextColor; + highlightedCandidateBackColor = [config getColorForOption:[prefix stringByAppendingString: + @"/hilited_candidate_back_color"]] ? : highlightedCandidateBackColor; + highlightedCandidateTextColor = [config getColorForOption:[prefix stringByAppendingString: + @"/hilited_candidate_text_color"]] ? : highlightedCandidateTextColor; + highlightedCommentTextColor = [config getColorForOption:[prefix stringByAppendingString: + @"/hilited_comment_text_color"]] ? : highlightedCommentTextColor; // for backward compatibility, 'label_hilited_color' and 'hilited_candidate_label_color' are both valid - highlightedCandidateLabelColor = [config getColor:[prefix stringByAppendingString:@"/label_hilited_color"]] ? : - [config getColor:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]] ? : highlightedCandidateLabelColor; - backImage = [config getImage:[prefix stringByAppendingString:@"/back_image"]] ? : backImage; + highlightedCandidateLabelColor = [config getColorForOption:[prefix stringByAppendingString:@"/label_hilited_color"]] ? : + [config getColorForOption:[prefix stringByAppendingString:@"/hilited_candidate_label_color"]] ? : highlightedCandidateLabelColor; + backImage = [config getImageForOption:[prefix stringByAppendingString:@"/back_image"]] ? : backImage; // the following per-color-scheme configurations, if exist, will // override configurations with the same name under the global 'style' section // INTERFACE override - updateCandidateListLayout(&linear, &tabled, config, prefix); + updateCandidateListLayout(&linear, &tabular, config, prefix); updateTextOrientation(&vertical, config, prefix); - inlinePreedit = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_preedit"]] ? : inlinePreedit; - inlineCandidate = [config getOptionalBool:[prefix stringByAppendingString:@"/inline_candidate"]] ? : inlineCandidate; - showPaging = [config getOptionalBool:[prefix stringByAppendingString:@"/show_paging"]] ? : showPaging; - rememberSize = [config getOptionalBool:[prefix stringByAppendingString:@"/remember_size"]] ? : rememberSize; - statusMessageType = [config getString:[prefix stringByAppendingString:@"/status_message_type"]] ? : statusMessageType; - candidateFormat = [config getString:[prefix stringByAppendingString:@"/candidate_format"]] ? : candidateFormat; + inlinePreedit = [config getOptionalBoolForOption:[prefix stringByAppendingString:@"/inline_preedit"]] ? : inlinePreedit; + inlineCandidate = [config getOptionalBoolForOption:[prefix stringByAppendingString:@"/inline_candidate"]] ? : inlineCandidate; + showPaging = [config getOptionalBoolForOption:[prefix stringByAppendingString:@"/show_paging"]] ? : showPaging; + rememberSize = [config getOptionalBoolForOption:[prefix stringByAppendingString:@"/remember_size"]] ? : rememberSize; + statusMessageType = [config getStringForOption:[prefix stringByAppendingString:@"/status_message_type"]] ? : statusMessageType; + candidateFormat = [config getStringForOption:[prefix stringByAppendingString:@"/candidate_format"]] ? : candidateFormat; // TYPOGRAPHY override - fontName = [config getString:[prefix stringByAppendingString:@"/font_face"]] ? : fontName; - fontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/font_point"] - applyConstraint:pos_round] ? : fontSize; - labelFontName = [config getString:[prefix stringByAppendingString:@"/label_font_face"]] ? : labelFontName; - labelFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/label_font_point"] - applyConstraint:pos_round] ? : labelFontSize; - commentFontName = [config getString:[prefix stringByAppendingString:@"/comment_font_face"]] ? : commentFontName; - commentFontSize = [config getOptionalDouble:[prefix stringByAppendingString:@"/comment_font_point"] - applyConstraint:pos_round] ? : commentFontSize; - alpha = [config getOptionalDouble:[prefix stringByAppendingString:@"/alpha"] - applyConstraint:clamp_uni] ? : alpha; - translucency = [config getOptionalDouble:[prefix stringByAppendingString:@"/translucency"] - applyConstraint:clamp_uni] ? : translucency; - cornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/corner_radius"] - applyConstraint:positive] ? : cornerRadius; - highlightedCornerRadius = [config getOptionalDouble:[prefix stringByAppendingString:@"/hilited_corner_radius"] - applyConstraint:positive] ? : highlightedCornerRadius; - borderHeight = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_height"] - applyConstraint:pos_ceil] ? : borderHeight; - borderWidth = [config getOptionalDouble:[prefix stringByAppendingString:@"/border_width"] - applyConstraint:pos_ceil] ? : borderWidth; - lineSpacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_spacing"] - applyConstraint:pos_round] ? : lineSpacing; - spacing = [config getOptionalDouble:[prefix stringByAppendingString:@"/spacing"] - applyConstraint:pos_round] ? : spacing; - baseOffset = [config getOptionalDouble:[prefix stringByAppendingString:@"/base_offset"]] ? : baseOffset; - lineLength = [config getOptionalDouble:[prefix stringByAppendingString:@"/line_length"]] ? : lineLength; + fontName = [config getStringForOption:[prefix stringByAppendingString:@"/font_face"]] ? : fontName; + fontSize = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/font_point"] + applyConstraint:pos_round] ? : fontSize; + labelFontName = [config getStringForOption:[prefix stringByAppendingString:@"/label_font_face"]] ? : labelFontName; + labelFontSize = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/label_font_point"] + applyConstraint:pos_round] ? : labelFontSize; + commentFontName = [config getStringForOption:[prefix stringByAppendingString:@"/comment_font_face"]] ? : commentFontName; + commentFontSize = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/comment_font_point"] + applyConstraint:pos_round] ? : commentFontSize; + alpha = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/alpha"] + applyConstraint:clamp_uni] ? : alpha; + translucency = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/translucency"] + applyConstraint:clamp_uni] ? : translucency; + cornerRadius = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/corner_radius"] + applyConstraint:positive] ? : cornerRadius; + highlightedCornerRadius = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/hilited_corner_radius"] + applyConstraint:positive] ? : highlightedCornerRadius; + borderHeight = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/border_height"] + applyConstraint:pos_ceil] ? : borderHeight; + borderWidth = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/border_width"] + applyConstraint:pos_ceil] ? : borderWidth; + lineSpacing = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/line_spacing"] + applyConstraint:pos_round] ? : lineSpacing; + spacing = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/spacing"] + applyConstraint:pos_round] ? : spacing; + baseOffset = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/base_offset"]] ? : baseOffset; + lineLength = [config getOptionalDoubleForOption:[prefix stringByAppendingString:@"/line_length"]] ? : lineLength; } // TYPOGRAPHY refinement @@ -3602,8 +3830,8 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditBackColor = preeditBackColor ? : isNative ? NSColor.windowBackgroundColor : nil; textColor = textColor ? : NSColor.textColor; candidateTextColor = candidateTextColor ? : NSColor.controlTextColor; - commentTextColor = commentTextColor ? : SquirrelTheme.secondaryTextColor; - candidateLabelColor = candidateLabelColor ? : isNative ? SquirrelTheme.accentColor : blendColors(candidateTextColor, backColor); + commentTextColor = commentTextColor ? : NSColor.secondaryTextColor; + candidateLabelColor = candidateLabelColor ? : isNative ? NSColor.accentColor : blendColors(candidateTextColor, backColor); highlightedBackColor = highlightedBackColor ? : isNative ? NSColor.selectedTextBackgroundColor : nil; highlightedTextColor = highlightedTextColor ? : NSColor.selectedTextColor; highlightedCandidateBackColor = highlightedCandidateBackColor ? : isNative ? NSColor.selectedContentBackgroundColor : nil; @@ -3630,15 +3858,15 @@ + (void)updateTheme:(SquirrelTheme *)theme [theme setCornerRadius:MIN(cornerRadius.doubleValue, lineHeight * 0.5) highlightedCornerRadius:MIN(highlightedCornerRadius.doubleValue, lineHeight * 0.5) separatorWidth:separatorWidth - borderInset:borderInset linespace:lineSpacing.doubleValue preeditLinespace:spacing.doubleValue alpha:alpha ? alpha.doubleValue : 1.0 translucency:translucency.doubleValue lineLength:lineLength.doubleValue > 0 ? MAX(ceil(lineLength.doubleValue), separatorWidth * 5) : 0.0 + borderInset:borderInset showPaging:showPaging.boolValue rememberSize:rememberSize.boolValue - tabled:tabled + tabular:tabular linear:linear vertical:vertical inlinePreedit:inlinePreedit.boolValue diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index a2454b3a8..1d82a21da 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -10,10 +10,13 @@ Revert previous modifications to see if the problem recurs."; "say_voice" = "Alex"; -"candidate" = "Click a candidate to ⎆select.\nSecondary click to ⎌forget selected word."; +"candidate" = "Click a candidate to ⎆select.\nSecondary click to ⎌forget selected word.\nPress and hold the ⌥option key to temporarily disable mouse interactions."; "delete" = "Click to ⌫Delete the input by character.\nSecondary click to ⎋Escape the composing."; "escape" = "Cannot delete any further.\nSecondary click to ⎋Escape the composing."; "page_up" = "Click to ⇞Page Up.\nSecondary click to jump to ↖Home."; "home" = "Cannot page up any further.\nSecondary click to jump to ↖Home."; "page_down" = "Click to ⇞Page Down.\nSecondary click to jump to ↘End."; "end" = "Cannot page down any further.\nSecondary click to jump to ↘End."; +"compress" = "Click to compress candidate window.\nSecondary click to lock this multiple-row view"; +"expand" = "Click to expand candidate window.\nSecondary click to lock this single-row view."; +"unlock" = "Click to unlock the view and allow it to be expanded or collapsed."; diff --git a/zh-HK.lproj/Localizable.strings b/zh-HK.lproj/Localizable.strings index 45818887d..81bd4f444 100644 --- a/zh-HK.lproj/Localizable.strings +++ b/zh-HK.lproj/Localizable.strings @@ -10,10 +10,13 @@ 請嘗試回退先前的改動,然後查看問題是否仍然存在。"; "say_voice" = "Sinji"; -"candidate" = "點按以⎆選取候選字。\n點按輔助按鈕以⎌清除所選的記憶字詞。"; +"candidate" = "點按以⎆選取候選字。\n點按輔助按鈕以⎌清除所選的記憶字詞。按住⌥Option鍵以暫時停用滑鼠與「鼠鬚筆」互動"; "delete" = "點按以逐字⌫刪除輸入。\n點按輔助按鈕以⎋取消輸入。"; "escape" = "無法再刪除。\n點按輔助按鈕以⎋取消輸入。"; "page_up" = "點按以⇞向上翻頁。\n點按輔助按鈕以跳至↖起點。"; "home" = "無法再向上翻頁。\n點按輔助按鈕以跳至↖起點。"; "page_down" = "點按以⇟向下翻頁。\n點按輔助按鈕以跳至↘結尾。"; "end" = "無法再向下翻頁。\n點按輔助按鈕以跳至↘結尾。"; +"compress" = "點按以收合候選字視窗。點按輔助按鈕以鎖定當前的多橫列顯示方式。"; +"expand" = "點按以展開候選字視窗。點按輔助按鈕以鎖定當前的單橫列顯示方式。"; +"unlock" = "點按以解鎖顯示方式,允許展開或收合候選字視窗。"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index cf655dc1b..e61410eed 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -10,10 +10,13 @@ 请尝试撤销之前的修改,然后查看问题是否仍旧存在。"; "say_voice" = "TingTing"; -"candidate" = "点按以⎆选择候选字。\n辅助点按以⎌删除所选的记忆字词。"; +"candidate" = "点按以⎆选择候选字。\n辅助点按以⎌删除所选的记忆字词。按住⌥Option键以暂时停用鼠标与“鼠须管”互动。"; "delete" = "点按以逐字⌫删除输入。\n辅助点按以⎋取消输入。"; "escape" = "不能再删除。\n辅助点按以⎋取消输入。"; "page_up" = "点按以⇞向上翻页。\n辅助点按以跳到↖开头。"; "home" = "不能再向上翻页。\n辅助点按以跳到↖开头。"; "page_down" = "点按以⇟向下翻页。\n辅助点按以跳到↘结尾。"; "end" = "不能再向下翻页。\n辅助点按以跳到↘结尾。"; +"compress" = "点按以折叠候选字窗口。辅助点按以锁定当前的多行视图。"; +"expand" = "点按以展开候选字窗口。辅助点按以锁定当前的单行视图。"; +"unlock" = "点按以解锁视图,允许展开或折叠候选字窗口。"; diff --git a/zh-Hant.lproj/Localizable.strings b/zh-Hant.lproj/Localizable.strings index 3124322d8..17ebacdaa 100644 --- a/zh-Hant.lproj/Localizable.strings +++ b/zh-Hant.lproj/Localizable.strings @@ -10,10 +10,13 @@ 請嘗試回退先前的修改,然後查看問題是否依然存在。"; "say_voice" = "MeiJia"; -"candidate" = "點按來⎆選取候選字。\n點按輔助按鈕來⎌清除所選的記憶字詞。"; +"candidate" = "點按來⎆選取候選字。\n點按輔助按鈕來⎌清除所選的記憶字詞。按住⌥Option鍵以暫時停用滑鼠與「鼠鬚管」互動。"; "delete" = "點按來逐字⌫刪除輸入。\n點按輔助按鈕來⎋取消輸入。"; "escape" = "無法再刪除。\n點按輔助按鈕來⎋取消輸入。"; "page_up" = "點按來⇞向上翻頁。\n點按輔助按鈕來跳至↖起始處。"; "home" = "無法再向上翻頁。\n點按輔助按鈕來跳至↖起始處。"; "page_down" = "點按來⇟向下翻頁。\n點按輔助按鈕來跳至↘結尾處。"; "end" = "無法再向下翻頁。\n點按輔助按鈕來跳至↘結尾處。"; +"compress" = "點按來收合候選字視窗。點按輔助按鈕來鎖定當前的多橫列顯示方式。"; +"expand" = "點按來展開候選字視窗。點按輔助按鈕來鎖定當前的單橫列顯示方式。"; +"unlock" = "點按來解鎖顯示方式,允許展開或收合候選字視窗。";

    *%5bTltcP?2-w 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%0DTi5~wBk0ivEh6%q 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