From 3987cad35e31c4656d2316c5672e69a6fe9cc215 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 08:51:42 +0800 Subject: [PATCH 01/21] Repo // Handle NSInternalInconsistencyException. --- .../ControllerModules/NSEventExtension.swift | 12 +++- .../ctlInputMethod_Common.swift | 55 +--------------- .../ctlInputMethod_Core.swift | 66 +++++++++++++++++-- 3 files changed, 69 insertions(+), 64 deletions(-) diff --git a/Source/Modules/ControllerModules/NSEventExtension.swift b/Source/Modules/ControllerModules/NSEventExtension.swift index 98e232f9..313698ed 100644 --- a/Source/Modules/ControllerModules/NSEventExtension.swift +++ b/Source/Modules/ControllerModules/NSEventExtension.swift @@ -22,15 +22,19 @@ extension NSEvent { isARepeat: Bool? = nil, keyCode: UInt16? = nil ) -> NSEvent? { - NSEvent.keyEvent( + let oldChars: String = { + if self.type == .flagsChanged { return "" } + return self.characters ?? "" + }() + return NSEvent.keyEvent( with: type ?? self.type, location: location ?? locationInWindow, modifierFlags: modifierFlags ?? self.modifierFlags, timestamp: timestamp ?? self.timestamp, windowNumber: windowNumber ?? self.windowNumber, context: nil, - characters: characters ?? self.characters ?? "", - charactersIgnoringModifiers: charactersIgnoringModifiers ?? self.characters ?? "", + characters: characters ?? oldChars, + charactersIgnoringModifiers: charactersIgnoringModifiers ?? characters ?? oldChars, isARepeat: isARepeat ?? self.isARepeat, keyCode: keyCode ?? self.keyCode ) @@ -81,6 +85,8 @@ extension NSEvent: InputSignalProtocol { } public var charCode: UInt16 { + guard type != .flagsChanged else { return 0 } + guard characters != nil else { return 0 } // 這裡不用「count > 0」,因為該整數變數只要「!isEmpty」那就必定滿足這個條件。 guard !text.isEmpty else { return 0 } let scalars = text.unicodeScalars diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Common.swift b/Source/Modules/ControllerModules/ctlInputMethod_Common.swift index 85db9525..02f2fb24 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Common.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Common.swift @@ -16,59 +16,9 @@ extension ctlInputMethod { /// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。 /// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。 func commonEventHandler(_ event: NSEvent) -> Bool { - // 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。 - let shouldUseHandle: Bool = { - switch mgrPrefs.shiftKeyAccommodationBehavior { - case 0: return false - case 1: return IME.arrClientShiftHandlingExceptionList.contains(clientBundleIdentifier) - case 2: return true - default: return false - } - }() - - if #available(macOS 10.15, *) { - if ShiftKeyUpChecker.check(event), !mgrPrefs.disableShiftTogglingAlphanumericalMode { - if !shouldUseHandle || (!rencentKeyHandledByKeyHandler && shouldUseHandle) { - NotifierController.notify( - message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n" - + (toggleASCIIMode() - ? NSLocalizedString("NotificationSwitchON", comment: "") - : NSLocalizedString("NotificationSwitchOFF", comment: "")) - ) - } - if shouldUseHandle { - rencentKeyHandledByKeyHandler = false - } - return false - } - } - - /// 沒有文字輸入客體的話,就不要再往下處理了。 - guard client() != nil else { return false } - - var event = event - // 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。 - if event.isEmacsKey { - let verticalProcessing = - (state.isCandidateContainer) - ? ctlInputMethod.isVerticalCandidateSituation : ctlInputMethod.isVerticalTypingSituation - event = event.convertFromEmacKeyEvent(isVerticalContext: verticalProcessing) - } - - /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 - /// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。 - /// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false, - /// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。 - if event.type == .flagsChanged { return false } - - // 準備修飾鍵,用來判定要新增的詞彙是否需要賦以非常低的權重。 - ctlInputMethod.areWeNerfing = event.modifierFlags.contains([.shift, .command]) - // 無法列印的訊號輸入,一概不作處理。 // 這個過程不能放在 KeyHandler 內,否則不會起作用。 - if !event.charCode.isPrintable { - return false - } + if !event.charCode.isPrintable { return false } /// 將按鍵行為與當前輸入法狀態結合起來、交給按鍵調度模組來處理。 /// 再根據返回的 result bool 數值來告知 IMK「這個按鍵事件是被處理了還是被放行了」。 @@ -78,9 +28,6 @@ extension ctlInputMethod { } errorCallback: { clsSFX.beep() } - if shouldUseHandle { - rencentKeyHandledByKeyHandler = result - } return result } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 8b975c43..0c30981e 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -57,7 +57,7 @@ class ctlInputMethod: IMKInputController { } /// `handle(event:)` 會利用這個參數判定某次 Shift 按鍵是否用來切換中英文輸入。 - var rencentKeyHandledByKeyHandler = false + var rencentKeyHandledByKeyHandlerEtc = false // MARK: - 工具函式 @@ -207,6 +207,8 @@ class ctlInputMethod: IMKInputController { @objc(handleEvent:client:) override func handle(_ event: NSEvent!, client sender: Any!) -> Bool { _ = sender // 防止格式整理工具毀掉與此對應的參數。 + // MARK: 前置處理 + // 更新此時的靜態狀態標記。 ctlInputMethod.isASCIIModeSituation = isASCIIMode ctlInputMethod.isVerticalTypingSituation = isVerticalTyping @@ -218,17 +220,63 @@ class ctlInputMethod: IMKInputController { return false } + // 用 Shift 開關半形英數模式,僅對 macOS 10.15 及之後的 macOS 有效。 + let shouldUseShiftToggleHandle: Bool = { + switch mgrPrefs.shiftKeyAccommodationBehavior { + case 0: return false + case 1: return IME.arrClientShiftHandlingExceptionList.contains(clientBundleIdentifier) + case 2: return true + default: return false + } + }() + + if #available(macOS 10.15, *) { + if ShiftKeyUpChecker.check(event), !mgrPrefs.disableShiftTogglingAlphanumericalMode { + if !shouldUseShiftToggleHandle || (!rencentKeyHandledByKeyHandlerEtc && shouldUseShiftToggleHandle) { + NotifierController.notify( + message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n" + + (toggleASCIIMode() + ? NSLocalizedString("NotificationSwitchON", comment: "") + : NSLocalizedString("NotificationSwitchOFF", comment: "")) + ) + } + if shouldUseShiftToggleHandle { + rencentKeyHandledByKeyHandlerEtc = false + } + return false + } + } + + // MARK: 針對客體的具體處理 + + /// 這裡仍舊需要判斷 flags。之前使輸入法狀態卡住無法敲漢字的問題已在 KeyHandler 內修復。 + /// 這裡不判斷 flags 的話,用方向鍵前後定位光標之後,再次試圖觸發組字區時、反而會在首次按鍵時失敗。 + /// 同時注意:必須在 event.type == .flagsChanged 結尾插入 return false, + /// 否則,每次處理這種判斷時都會觸發 NSInternalInconsistencyException。 + if event.type == .flagsChanged { return false } + + /// 沒有文字輸入客體的話,就不要再往下處理了。 + guard client() != nil else { return false } + + var eventToDeal = event + // 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。 + if eventToDeal.isEmacsKey { + let verticalProcessing = + (state.isCandidateContainer) + ? ctlInputMethod.isVerticalCandidateSituation : ctlInputMethod.isVerticalTypingSituation + eventToDeal = eventToDeal.convertFromEmacKeyEvent(isVerticalContext: verticalProcessing) + } + + // 準備修飾鍵,用來判定要新增的詞彙是否需要賦以非常低的權重。 + ctlInputMethod.areWeNerfing = eventToDeal.modifierFlags.contains([.shift, .command]) + // IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。 // 這樣可以讓 interpretKeyEvents() 函式自行判斷: // - 是就地交給 super.interpretKeyEvents() 處理? // - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理? proc: if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK { guard ctlCandidateCurrent.visible else { break proc } - var event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: event) ?? event - // 使 NSEvent 自翻譯,這樣可以讓 Emacs NSEvent 變成標準 NSEvent。 - if event.isEmacsKey { - event = event.convertFromEmacKeyEvent(isVerticalContext: ctlInputMethod.isVerticalCandidateSituation) - } + var event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal // Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。 // 所以這裡直接將 Shift Flags 清空。 @@ -255,7 +303,11 @@ class ctlInputMethod: IMKInputController { /// 我們不在這裡處理了,直接交給 commonEventHandler 來處理。 /// 這樣可以與 IMK 選字窗共用按鍵處理資源,維護起來也比較方便。 /// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。 - return commonEventHandler(event) + let result = commonEventHandler(eventToDeal) + if shouldUseShiftToggleHandle { + rencentKeyHandledByKeyHandlerEtc = result + } + return result } /// 有時會出現某些 App 攔截輸入法的 Ctrl+Enter / Shift+Enter 熱鍵的情況。 From e417be73bb9ca7e60dfbce2676edb1992552c9f1 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 10:08:05 +0800 Subject: [PATCH 02/21] ctlIME // Optimize signal flow used in handle(event:). --- .../ctlInputMethod_Common.swift | 37 +++++++++++++++++++ .../ctlInputMethod_Core.swift | 34 +++-------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Common.swift b/Source/Modules/ControllerModules/ctlInputMethod_Common.swift index 02f2fb24..443078b9 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Common.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Common.swift @@ -30,4 +30,41 @@ extension ctlInputMethod { } return result } + + /// 完成 handle() 函式本該完成的內容,但專門處理與 IMK 選字窗有關的判斷語句。 + /// 這樣分開處理很有必要,不然 handle() 函式會陷入無限迴圈。 + /// - Parameter event: 由 IMK 選字窗接收的裝置操作輸入事件。 + /// - Returns: 回「`true`」以將該案件已攔截處理的訊息傳遞給 IMK;回「`false`」則放行、不作處理。 + func imkCandidatesEventHandler(event eventToDeal: NSEvent) -> Bool? { + // IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。 + // 這樣可以讓 interpretKeyEvents() 函式自行判斷: + // - 是就地交給 super.interpretKeyEvents() 處理? + // - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理? + if let imkCandidates = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK, imkCandidates.visible { + let event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal + + // Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。 + // 所以這裡直接將 Shift Flags 清空。 + if event.isShiftHold, event.isEnter { + guard let newEvent = event.reinitiate(modifierFlags: []) else { + NSSound.beep() + return true + } + imkCandidates.interpretKeyEvents([newEvent]) + return true + } + + // 聯想詞選字。 + if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode], + event.isShiftHold, isAssociatedPhrasesState, + let newEvent = event.reinitiate(modifierFlags: [], characters: newChar) + { + imkCandidates.handleKeyboardEvent(newEvent) + } + + imkCandidates.interpretKeyEvents([event]) + return true + } + return nil + } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 0c30981e..1ac90a02 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -230,6 +230,7 @@ class ctlInputMethod: IMKInputController { } }() + /// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。 if #available(macOS 10.15, *) { if ShiftKeyUpChecker.check(event), !mgrPrefs.disableShiftTogglingAlphanumericalMode { if !shouldUseShiftToggleHandle || (!rencentKeyHandledByKeyHandlerEtc && shouldUseShiftToggleHandle) { @@ -271,38 +272,15 @@ class ctlInputMethod: IMKInputController { ctlInputMethod.areWeNerfing = eventToDeal.modifierFlags.contains([.shift, .command]) // IMK 選字窗處理,當且僅當啟用了 IMK 選字窗的時候才會生效。 - // 這樣可以讓 interpretKeyEvents() 函式自行判斷: - // - 是就地交給 super.interpretKeyEvents() 處理? - // - 還是藉由 delegate 扔回 ctlInputMethod 給 KeyHandler 處理? - proc: if let ctlCandidateCurrent = ctlInputMethod.ctlCandidateCurrent as? ctlCandidateIMK { - guard ctlCandidateCurrent.visible else { break proc } - var event: NSEvent = ctlCandidateIMK.replaceNumPadKeyCodes(target: eventToDeal) ?? eventToDeal - - // Shift+Enter 是個特殊情形,不提前攔截處理的話、會有垃圾參數傳給 delegate 的 keyHandler 從而崩潰。 - // 所以這裡直接將 Shift Flags 清空。 - if event.isShiftHold, event.isEnter { - guard let newEvent = event.reinitiate(modifierFlags: []) else { - NSSound.beep() - return true - } - ctlCandidateCurrent.interpretKeyEvents([newEvent]) - return true + if let result = imkCandidatesEventHandler(event: eventToDeal) { + if shouldUseShiftToggleHandle { + rencentKeyHandledByKeyHandlerEtc = result } - - // 聯想詞選字。 - if let newChar = ctlCandidateIMK.defaultIMKSelectionKey[event.keyCode], event.isShiftHold, - isAssociatedPhrasesState, let newEvent = event.reinitiate(modifierFlags: [], characters: newChar) - { - ctlCandidateCurrent.handleKeyboardEvent(newEvent) - } - - ctlCandidateCurrent.interpretKeyEvents([event]) - return true + return result } - /// 我們不在這裡處理了,直接交給 commonEventHandler 來處理。 + /// 剩下的 NSEvent 直接交給 commonEventHandler 來處理。 /// 這樣可以與 IMK 選字窗共用按鍵處理資源,維護起來也比較方便。 - /// 警告:這裡的 event 必須是原始 event 且不能被 var,否則會影響 Shift 中英模式判定。 let result = commonEventHandler(eventToDeal) if shouldUseShiftToggleHandle { rencentKeyHandledByKeyHandlerEtc = result From 03d38a80ed85872ccaa0db1e1accb0badd0061f5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 07:56:58 +0800 Subject: [PATCH 03/21] NSAttrTextView // Enhance compatibility with macOS 10.11 El Capitan. - Also fine-tuning the line spacing. --- .../NSAttributedTextView/NSAttributedTextView.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/3rdParty/NSAttributedTextView/NSAttributedTextView.swift b/Source/3rdParty/NSAttributedTextView/NSAttributedTextView.swift index 67bc525e..f8c8be20 100644 --- a/Source/3rdParty/NSAttributedTextView/NSAttributedTextView.swift +++ b/Source/3rdParty/NSAttributedTextView/NSAttributedTextView.swift @@ -65,9 +65,11 @@ public class NSAttributedTextView: NSView { let isVertical: Bool = !(direction == .horizontal) newAttributes[.verticalGlyphForm] = isVertical let newStyle: NSMutableParagraphStyle = newAttributes[.paragraphStyle] as! NSMutableParagraphStyle - newStyle.lineSpacing = isVertical ? (fontSize / -2) : fontSize * 0.1 - newStyle.maximumLineHeight = fontSize - newStyle.minimumLineHeight = fontSize + if #available(macOS 10.13, *) { + newStyle.lineSpacing = isVertical ? (fontSize / -2) : fontSize * 0.1 + newStyle.maximumLineHeight = fontSize * 1.1 + newStyle.minimumLineHeight = fontSize * 1.1 + } newAttributes[.paragraphStyle] = newStyle var text: String = text ?? "" if !(direction == .horizontal) { From 4527e6f7baba8088c675a3235e8555d5f7aab14a Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 10:38:27 +0800 Subject: [PATCH 04/21] IME // Extend NSRect. --- Source/Modules/IMEModules/IME.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Modules/IMEModules/IME.swift b/Source/Modules/IMEModules/IME.swift index b86cd6dc..327a78c5 100644 --- a/Source/Modules/IMEModules/IME.swift +++ b/Source/Modules/IMEModules/IME.swift @@ -348,3 +348,11 @@ extension NSApplication { return output } } + +// MARK: NSRect Extension + +extension NSRect { + public static var seniorTheBeast: NSRect { + NSRect(x: 0.0, y: 0.0, width: 0.114, height: 0.514) + } +} From a846022f1bbe613223bf7571a98d748536e92057 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 11:09:32 +0800 Subject: [PATCH 05/21] Repo // Disable NSRect.zero to suppress CoreGraphics errors. --- .../ControllerModules/ctlInputMethod_Delegates.swift | 2 +- Source/Modules/UIModules/CandidateUI/ctlCandidate.swift | 2 +- .../Modules/UIModules/CandidateUI/ctlCandidateIMK.swift | 2 +- .../UIModules/CandidateUI/ctlCandidateUniversal.swift | 2 +- .../Modules/UIModules/NotifierUI/NotifierController.swift | 8 ++++---- Source/Modules/UIModules/TooltipUI/ctlTooltip.swift | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift index 1927a05d..9459b649 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Delegates.swift @@ -16,7 +16,7 @@ extension ctlInputMethod: KeyHandlerDelegate { /// 記錄當前輸入環境是縱排輸入還是橫排輸入。 public var isVerticalTyping: Bool { guard let client = client() else { return false } - var textFrame = NSRect.zero + var textFrame = NSRect.seniorTheBeast let attributes: [AnyHashable: Any]? = client.attributes( forCharacterIndex: 0, lineHeightRectangle: &textFrame ) diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift b/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift index 1f32722a..73d81745 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift +++ b/Source/Modules/UIModules/CandidateUI/ctlCandidate.swift @@ -160,7 +160,7 @@ public class ctlCandidate: NSWindowController, ctlCandidateProtocol { var adjustedPoint = windowTopLeftPoint var delta = heightDelta - var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero + var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast for frame in NSScreen.screens.map(\.visibleFrame).filter({ !$0.contains(windowTopLeftPoint) }) { screenFrame = frame break diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift b/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift index bae2b48d..a59fd18e 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift +++ b/Source/Modules/UIModules/CandidateUI/ctlCandidateIMK.swift @@ -167,7 +167,7 @@ public class ctlCandidateIMK: IMKCandidates, ctlCandidateProtocol { var adjustedPoint = windowTopLeftPoint var adjustedHeight = height - var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero + var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast for screen in NSScreen.screens { let frame = screen.visibleFrame if windowTopLeftPoint.x >= frame.minX, windowTopLeftPoint.x <= frame.maxX, diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift b/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift index 0590cc3b..916d4798 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift +++ b/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift @@ -600,7 +600,7 @@ extension ctlCandidateUniversal { pageCounterLabel.isHidden = true } - frameRect = window?.frame ?? NSRect.zero + frameRect = window?.frame ?? NSRect.seniorTheBeast let topLeftPoint = NSPoint(x: frameRect.origin.x, y: frameRect.origin.y + frameRect.size.height) frameRect.size = newSize diff --git a/Source/Modules/UIModules/NotifierUI/NotifierController.swift b/Source/Modules/UIModules/NotifierUI/NotifierController.swift index 070fc301..caafdd1a 100644 --- a/Source/Modules/UIModules/NotifierUI/NotifierController.swift +++ b/Source/Modules/UIModules/NotifierUI/NotifierController.swift @@ -90,7 +90,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { } private init() { - let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero + let screenRect = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast let contentRect = NSRect(x: 0, y: 0, width: kWindowWidth, height: kWindowHeight) var windowRect = contentRect windowRect.origin.x = screenRect.maxX - windowRect.width - 10 @@ -144,8 +144,8 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { return } let lastLocation = NotifierController.lastLocation - let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero - var windowRect = window?.frame ?? NSRect.zero + let screenRect = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast + var windowRect = window?.frame ?? NSRect.seniorTheBeast windowRect.origin.x = lastLocation.x windowRect.origin.y = lastLocation.y - 10 - windowRect.height @@ -157,7 +157,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { } func moveIn() { - let afterRect = window?.frame ?? NSRect.zero + let afterRect = window?.frame ?? NSRect.seniorTheBeast NotifierController.lastLocation = afterRect.origin var beforeRect = afterRect beforeRect.origin.y += 10 diff --git a/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift b/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift index 23b5df9b..0ef603f5 100644 --- a/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift +++ b/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift @@ -129,7 +129,7 @@ public class ctlTooltip: NSWindowController { var adjustedPoint = windowTopLeftPoint var delta = heightDelta - var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.zero + var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast for frame in NSScreen.screens.map(\.visibleFrame).filter({ !$0.contains(windowTopLeftPoint) }) { screenFrame = frame break From 6666e5ab8e0f77ff2f8a1fa5190c2e443e86555f Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 16:43:00 +0800 Subject: [PATCH 06/21] IMEStateData // Optimize kanji conversation handling. --- .../ControllerModules/IMEStateData.swift | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/Modules/ControllerModules/IMEStateData.swift b/Source/Modules/ControllerModules/IMEStateData.swift index 1ea5eac8..a96dc43e 100644 --- a/Source/Modules/ControllerModules/IMEStateData.swift +++ b/Source/Modules/ControllerModules/IMEStateData.swift @@ -9,13 +9,16 @@ import Foundation public struct StateData { - var displayedText: String = "" { - didSet { - let result = IME.kanjiConversionIfRequired(displayedText) - if result.utf16.count == displayedText.utf16.count, result.count == displayedText.count { - displayedText = result - } + var displayedText: String = "" + var displayedTextConverted: String { + /// 先做繁簡轉換 + var result = IME.kanjiConversionIfRequired(displayedText) + if result.utf16.count != displayedText.utf16.count + || result.count != displayedText.count + { + result = displayedText } + return result } // MARK: Cursor & Marker & Range for UTF8 @@ -86,7 +89,7 @@ public struct StateData { var attributedStringNormal: NSAttributedString { /// 考慮到因為滑鼠點擊等其它行為導致的組字區內容遞交情況, /// 這裡對組字區內容也加上康熙字轉換或者 JIS 漢字轉換處理。 - let attributedString = NSMutableAttributedString(string: displayedText) + let attributedString = NSMutableAttributedString(string: displayedTextConverted) var newBegin = 0 for (i, neta) in displayTextSegments.enumerated() { attributedString.setAttributes( @@ -104,7 +107,7 @@ public struct StateData { var attributedStringMarking: NSAttributedString { /// 考慮到因為滑鼠點擊等其它行為導致的組字區內容遞交情況, /// 這裡對組字區內容也加上康熙字轉換或者 JIS 漢字轉換處理。 - let attributedString = NSMutableAttributedString(string: displayedText) + let attributedString = NSMutableAttributedString(string: displayedTextConverted) let end = u16MarkedRange.upperBound attributedString.setAttributes( @@ -130,7 +133,7 @@ public struct StateData { ], range: NSRange( location: end, - length: displayedText.utf16.count - end + length: displayedTextConverted.utf16.count - end ) ) return attributedString From 782b39f3de2030e6c132ea5cd8ad1936456f7016 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 18:36:53 +0800 Subject: [PATCH 07/21] IMEState // Sync marker to cursor by default. --- Source/Modules/ControllerModules/IMEState.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Modules/ControllerModules/IMEState.swift b/Source/Modules/ControllerModules/IMEState.swift index ba950e97..eb4b5282 100644 --- a/Source/Modules/ControllerModules/IMEState.swift +++ b/Source/Modules/ControllerModules/IMEState.swift @@ -113,6 +113,7 @@ extension IMEState { // 注意資料的設定順序,一定得先設定 displayTextSegments。 result.data.displayTextSegments = displayTextSegments result.data.cursor = cursor + result.data.marker = cursor return result } From ba524ea451c5628c28f977ceef6c060444ce1b7d Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 17:15:05 +0800 Subject: [PATCH 08/21] mgrPrefs // +clientsIMKTextInputIncapable. --- Source/Modules/IMEModules/mgrPrefs.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Source/Modules/IMEModules/mgrPrefs.swift b/Source/Modules/IMEModules/mgrPrefs.swift index 1ad954d3..b311d12e 100644 --- a/Source/Modules/IMEModules/mgrPrefs.swift +++ b/Source/Modules/IMEModules/mgrPrefs.swift @@ -56,6 +56,7 @@ public enum UserDef: String, CaseIterable { case kHardenVerticalPunctuations = "HardenVerticalPunctuations" case kTrimUnfinishedReadingsOnCommit = "TrimUnfinishedReadingsOnCommit" case kAlwaysShowTooltipTextsHorizontally = "AlwaysShowTooltipTextsHorizontally" + case kClientsIMKTextInputIncapable = "ClientsIMKTextInputIncapable" case kUseIMKCandidateWindow = "UseIMKCandidateWindow" case kHandleDefaultCandidateFontsByLangIdentifier = "HandleDefaultCandidateFontsByLangIdentifier" @@ -306,6 +307,9 @@ public enum mgrPrefs { UserDefaults.standard.setDefault( mgrPrefs.trimUnfinishedReadingsOnCommit, forKey: UserDef.kTrimUnfinishedReadingsOnCommit.rawValue ) + UserDefaults.standard.setDefault( + mgrPrefs.clientsIMKTextInputIncapable, forKey: UserDef.kClientsIMKTextInputIncapable.rawValue + ) // ----- @@ -443,6 +447,9 @@ public enum mgrPrefs { @UserDefault(key: UserDef.kAlwaysShowTooltipTextsHorizontally.rawValue, defaultValue: false) static var alwaysShowTooltipTextsHorizontally: Bool + @UserDefault(key: UserDef.kClientsIMKTextInputIncapable.rawValue, defaultValue: ["com.valvesoftware.steam"]) + static var clientsIMKTextInputIncapable: [String] + // MARK: - Settings (Tier 2) @UserDefault(key: UserDef.kUseIMKCandidateWindow.rawValue, defaultValue: false) @@ -738,6 +745,8 @@ extension mgrPrefs { mgrPrefs.disableShiftTogglingAlphanumericalMode = false mgrPrefs.togglingAlphanumericalModeWithLShift = false } + // 客體黑名單自動排序去重複。 + clientsIMKTextInputIncapable = Array(Set(clientsIMKTextInputIncapable)).sorted() // 注拼槽注音排列選項糾錯。 var isMandarinParserOptionValid = false MandarinParser.allCases.forEach { From a2f0b948790e7e8f6a3422123e80e837c4d75c11 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 17:20:57 +0800 Subject: [PATCH 09/21] KeyHandler // Reimplement commitOverflownComposition(). - Also handle reading-mismatched nodes. --- .../ControllerModules/KeyHandler_Core.swift | 49 +++++++++++++++++++ .../KeyHandler_HandleComposition.swift | 6 ++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/KeyHandler_Core.swift b/Source/Modules/ControllerModules/KeyHandler_Core.swift index d52cb05d..6a237942 100644 --- a/Source/Modules/ControllerModules/KeyHandler_Core.swift +++ b/Source/Modules/ControllerModules/KeyHandler_Core.swift @@ -438,3 +438,52 @@ public class KeyHandler { } } } + +// MARK: - Components for Popup Composition Buffer (PCB) Window. + +/// 組字區文字上限。 +/// - Remark: 該選項僅對不支援 IMKTextInput 協定的應用有用,就不交給 mgrPrefs 了。 +private let compositorWidthLimit = 20 + +extension KeyHandler { + /// 在爬取組字結果之前,先將即將從組字區溢出的內容遞交出去。 + /// + /// 在理想狀況之下,組字區多長都無所謂。但是,螢幕浮動組字窗的尺寸是有限的。 + /// 於是,有必要限定組字區的長度。超過該長度的內容會在爬軌之前先遞交出去, + /// 使其不再記入最大相似度估算的估算對象範圍。 + /// 用比較形象且生動卻有點噁心的解釋的話,蒼蠅一邊吃一邊屙。 + var commitOverflownComposition: String { + guard !compositor.walkedNodes.isEmpty, + compositor.width > compositorWidthLimit, + let identifier = delegate?.clientBundleIdentifier, + mgrPrefs.clientsIMKTextInputIncapable.contains(identifier) + else { + return "" + } + // 回頭在這裡插上對 Steam 的 Client Identifier 的要求。 + var textToCommit = "" + while compositor.width > compositorWidthLimit { + var delta = compositor.width - compositorWidthLimit + let node = compositor.walkedNodes[0] + if node.isReadingMismatched { + delta = node.keyArray.count + textToCommit += node.currentPair.value + } else { + delta = min(delta, node.keyArray.count) + textToCommit += node.currentPair.value.charComponents[0.. Date: Sat, 10 Sep 2022 19:07:33 +0800 Subject: [PATCH 10/21] ctlIME // Fix wrong calculation of utf16Cursors with lineHeightRect. --- .../ControllerModules/ctlInputMethod_HandleDisplay.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift index 3f50bba0..a969cc21 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift @@ -17,7 +17,7 @@ extension ctlInputMethod { guard let client = client() else { return } var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) var cursor = u16Cursor - if cursor == displayedText.count, cursor != 0 { + if cursor == displayedText.utf16.count, cursor != 0 { cursor -= 1 } while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { @@ -124,8 +124,8 @@ extension ctlInputMethod { var cursor = 0 if [.ofCandidates, .ofSymbolTable].contains(state.type) { - cursor = state.data.cursor - if cursor == state.displayedText.count, cursor != 0 { + cursor = state.data.u16Cursor + if cursor == state.displayedText.utf16.count, cursor != 0 { cursor -= 1 } } From d52678c9592b71d976e49b17b35beb3d6ae58abc Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 19:13:45 +0800 Subject: [PATCH 11/21] ctlIME // Add "lineHeightRect" and simplify display process. --- .../ctlInputMethod_HandleDisplay.swift | 55 +++++++++---------- .../ctlInputMethod_HandleStates.swift | 15 +---- 2 files changed, 28 insertions(+), 42 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift index a969cc21..7e7d25b2 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleDisplay.swift @@ -13,19 +13,31 @@ import Cocoa // MARK: - Tooltip Display and Candidate Display Methods extension ctlInputMethod { - func show(tooltip: String, displayedText: String, u16Cursor: Int) { - guard let client = client() else { return } - var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) - var cursor = u16Cursor - if cursor == displayedText.utf16.count, cursor != 0 { - cursor -= 1 + func lineHeightRect(zeroCursor: Bool = false) -> NSRect { + var lineHeightRect = NSRect.seniorTheBeast + guard let client = client() else { + return lineHeightRect } - while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { + var u16Cursor: Int = { + // iMessage 在 cursor == 0 時的計算會有一些偏差,所以例外處理。 + if clientBundleIdentifier == "com.apple.MobileSMS" { return state.data.u16Cursor } + if state.data.marker >= state.data.cursor { return state.data.u16Cursor } + return state.data.u16Marker // 這樣可以讓工具提示視窗始終盡量往書寫方向的後方顯示。 + }() + u16Cursor = max(min(state.data.displayedTextConverted.utf16.count, u16Cursor), 0) + if zeroCursor { u16Cursor = 0 } + while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, u16Cursor >= 0 { client.attributes( - forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect + forCharacterIndex: u16Cursor, lineHeightRectangle: &lineHeightRect ) - cursor -= 1 + u16Cursor -= 1 } + return lineHeightRect + } + + func show(tooltip: String) { + guard client() != nil else { return } + let lineHeightRect = lineHeightRect() var finalOrigin: NSPoint = lineHeightRect.origin let delta: CGFloat = lineHeightRect.size.height + 4.0 // bottomOutOfScreenAdjustmentHeight if isVerticalTyping { @@ -120,34 +132,17 @@ extension ctlInputMethod { ctlInputMethod.ctlCandidateCurrent.visible = true - var lineHeightRect = NSRect(x: 0.0, y: 0.0, width: 16.0, height: 16.0) - var cursor = 0 - - if [.ofCandidates, .ofSymbolTable].contains(state.type) { - cursor = state.data.u16Cursor - if cursor == state.displayedText.utf16.count, cursor != 0 { - cursor -= 1 - } - } - - while lineHeightRect.origin.x == 0, lineHeightRect.origin.y == 0, cursor >= 0 { - client.attributes( - forCharacterIndex: cursor, lineHeightRectangle: &lineHeightRect - ) - cursor -= 1 - } - if isVerticalTyping { ctlInputMethod.ctlCandidateCurrent.set( windowTopLeftPoint: NSPoint( - x: lineHeightRect.origin.x + lineHeightRect.size.width + 4.0, y: lineHeightRect.origin.y - 4.0 + x: lineHeightRect().origin.x + lineHeightRect().size.width + 4.0, y: lineHeightRect().origin.y - 4.0 ), - bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0 + bottomOutOfScreenAdjustmentHeight: lineHeightRect().size.height + 4.0 ) } else { ctlInputMethod.ctlCandidateCurrent.set( - windowTopLeftPoint: NSPoint(x: lineHeightRect.origin.x, y: lineHeightRect.origin.y - 4.0), - bottomOutOfScreenAdjustmentHeight: lineHeightRect.size.height + 4.0 + windowTopLeftPoint: NSPoint(x: lineHeightRect().origin.x, y: lineHeightRect().origin.y - 4.0), + bottomOutOfScreenAdjustmentHeight: lineHeightRect().size.height + 4.0 ) } } diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift index 6bb1b30b..0c23eb42 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift @@ -65,10 +65,8 @@ extension ctlInputMethod { if !textToCommit.isEmpty { commit(text: textToCommit) } setInlineDisplayWithCursor() if !state.tooltip.isEmpty { - show( - tooltip: state.tooltip, displayedText: state.displayedText, - u16Cursor: state.data.u16Cursor - ) + show(tooltip: state.tooltip) + } } case .ofMarking: ctlInputMethod.ctlCandidateCurrent.visible = false @@ -76,14 +74,7 @@ extension ctlInputMethod { if state.tooltip.isEmpty { ctlInputMethod.tooltipInstance.hide() } else { - let cursorReference: Int = { - if state.data.marker >= state.data.cursor { return state.data.u16Cursor } - return state.data.u16Marker // 這樣可以讓工具提示視窗始終盡量往書寫方向的後方顯示。 - }() - show( - tooltip: state.tooltip, displayedText: state.displayedText, - u16Cursor: cursorReference - ) + show(tooltip: state.tooltip) } case .ofCandidates, .ofAssociates, .ofSymbolTable: ctlInputMethod.tooltipInstance.hide() From fc976a636316afc0dc99c0bf5f6b8b8c92251d04 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 21:44:34 +0800 Subject: [PATCH 12/21] ctlIME // Highlight the marked range. --- .../Modules/ControllerModules/ctlInputMethod_HandleStates.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift index 0c23eb42..37f80e8d 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift @@ -100,7 +100,7 @@ extension ctlInputMethod { /// 是 0 且取代範圍(replacementRange)為「NSNotFound」罷了。 /// 也就是說,內文組字區該在哪裡出現,得由客體軟體來作主。 client.setMarkedText( - state.attributedString, selectionRange: NSRange(location: state.data.u16Cursor, length: 0), + state.attributedString, selectionRange: NSRange(state.data.u16MarkedRange), replacementRange: NSRange(location: NSNotFound, length: NSNotFound) ) return From d98b2bbc349f676a9e0c5fc69c331c8baf55697e Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sat, 10 Sep 2022 19:38:49 +0800 Subject: [PATCH 13/21] Repo // Add ctlPopupCompositionBuffer. --- .../ctlInputMethod_Core.swift | 5 +- .../ctlInputMethod_HandleStates.swift | 9 +- .../CandidateUI/ctlCandidateUniversal.swift | 2 +- .../ctlPopupCompositionBuffer.swift | 127 ++++++++++++++++++ .../UIModules/TooltipUI/ctlTooltip.swift | 2 +- vChewing.xcodeproj/project.pbxproj | 12 ++ 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 Source/Modules/UIModules/PopupCompositionBufferUI/ctlPopupCompositionBuffer.swift diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 1ac90a02..4834e277 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -27,9 +27,12 @@ class ctlInputMethod: IMKInputController { static var ctlCandidateCurrent: ctlCandidateProtocol = mgrPrefs.useIMKCandidateWindow ? ctlCandidateIMK.init(.horizontal) : ctlCandidateUniversal.init(.horizontal) - /// 工具提示視窗的副本,每次都重新初始化。 + /// 工具提示視窗的共用副本。 static var tooltipInstance = ctlTooltip() + /// 浮動組字窗的共用副本。 + static var popupCompositionBuffer = ctlPopupCompositionBuffer() + // MARK: - /// 當前這個 ctlInputMethod 副本是否處於英數輸入模式(滯後項)。 diff --git a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift index 37f80e8d..12423441 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_HandleStates.swift @@ -67,7 +67,6 @@ extension ctlInputMethod { if !state.tooltip.isEmpty { show(tooltip: state.tooltip) } - } case .ofMarking: ctlInputMethod.ctlCandidateCurrent.visible = false setInlineDisplayWithCursor() @@ -82,6 +81,14 @@ extension ctlInputMethod { show(candidateWindowWith: state) default: break } + // 浮動組字窗的顯示判定 + if state.hasComposition, mgrPrefs.clientsIMKTextInputIncapable.contains(clientBundleIdentifier) { + ctlInputMethod.popupCompositionBuffer.show( + state: state, at: lineHeightRect(zeroCursor: true).origin + ) + } else { + ctlInputMethod.popupCompositionBuffer.hide() + } } /// 針對受 .NotEmpty() 管轄的非空狀態,在組字區內顯示游標。 diff --git a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift b/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift index 916d4798..caf269b7 100644 --- a/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift +++ b/Source/Modules/UIModules/CandidateUI/ctlCandidateUniversal.swift @@ -346,7 +346,7 @@ public class ctlCandidateUniversal: ctlCandidate { let panel = NSPanel( contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false ) - panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 2) panel.hasShadow = true panel.isOpaque = false panel.backgroundColor = NSColor.clear diff --git a/Source/Modules/UIModules/PopupCompositionBufferUI/ctlPopupCompositionBuffer.swift b/Source/Modules/UIModules/PopupCompositionBufferUI/ctlPopupCompositionBuffer.swift new file mode 100644 index 00000000..c9ce1870 --- /dev/null +++ b/Source/Modules/UIModules/PopupCompositionBufferUI/ctlPopupCompositionBuffer.swift @@ -0,0 +1,127 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import Cocoa + +public class ctlPopupCompositionBuffer: NSWindowController { + private var messageTextField: NSTextField + private var textShown: NSAttributedString = .init(string: "") { + didSet { + messageTextField.attributedStringValue = textShown + adjustSize() + } + } + + public init() { + let transparentVisualEffect = NSVisualEffectView() + transparentVisualEffect.blendingMode = .behindWindow + transparentVisualEffect.state = .active + let contentRect = NSRect(x: 128.0, y: 128.0, width: 300.0, height: 20.0) + let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] + let panel = NSPanel( + contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false + ) + panel.contentView = transparentVisualEffect + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) + panel.hasShadow = true + panel.backgroundColor = NSColor.clear + + messageTextField = NSTextField() + messageTextField.isEditable = false + messageTextField.isSelectable = false + messageTextField.isBezeled = false + messageTextField.textColor = NSColor.textColor + messageTextField.drawsBackground = true + messageTextField.backgroundColor = NSColor.clear + messageTextField.font = .systemFont(ofSize: 18) + panel.contentView?.addSubview(messageTextField) + super.init(window: panel) + } + + @available(*, unavailable) + public required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func show(state: IMEStateProtocol, at point: NSPoint) { + if !state.hasComposition { + hide() + return + } + // 在這個視窗內的下畫線繪製方法就得單獨設計了。 + let attrString: NSMutableAttributedString = .init(string: state.data.displayedTextConverted) + attrString.setAttributes( + [ + .backgroundColor: NSColor.alternateSelectedControlColor, + .foregroundColor: NSColor.alternateSelectedControlTextColor, + .markedClauseSegment: 0, + ], + range: NSRange( + location: state.data.u16MarkedRange.lowerBound, + length: state.data.u16MarkedRange.upperBound - state.data.u16MarkedRange.lowerBound + ) + ) + let attrCursor = NSMutableAttributedString(string: "_") + if #available(macOS 10.13, *) { + attrCursor.setAttributes( + [ + .kern: -18, + .baselineOffset: -2, + .markedClauseSegment: 1, + ], + range: NSRange(location: 0, length: attrCursor.string.utf16.count) + ) + } + attrString.insert(attrCursor, at: state.data.u16Cursor) + textShown = attrString + messageTextField.maximumNumberOfLines = 1 + if let editor = messageTextField.currentEditor() { + editor.selectedRange = NSRange(state.data.u16MarkedRange) + } + window?.orderFront(nil) + set(windowOrigin: point) + } + + public func hide() { + window?.orderOut(nil) + } + + private func set(windowOrigin: NSPoint) { + guard let window = window else { return } + let windowSize = window.frame.size + + var adjustedPoint = windowOrigin + var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast + for frame in NSScreen.screens.map(\.visibleFrame).filter({ !$0.contains(windowOrigin) }) { + screenFrame = frame + break + } + + adjustedPoint.y = min(max(adjustedPoint.y, screenFrame.minY + windowSize.height), screenFrame.maxY) + adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width) + + window.setFrameOrigin(adjustedPoint) + } + + private func adjustSize() { + let attrString = messageTextField.attributedStringValue + var rect = attrString.boundingRect( + with: NSSize(width: 1600.0, height: 1600.0), + options: [.usesLineFragmentOrigin, .usesFontLeading] + ) + rect.size.width = max(rect.size.width, 20 * CGFloat(attrString.string.count)) + 2 + rect.size.height = 22 + var bigRect = rect + bigRect.size.width += NSFont.systemFontSize + bigRect.size.height += NSFont.systemFontSize + rect.origin.x += NSFont.systemFontSize / 2 + rect.origin.y += NSFont.systemFontSize / 2 + messageTextField.frame = rect + window?.setFrame(bigRect, display: true) + } +} diff --git a/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift b/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift index 0ef603f5..2f41fc63 100644 --- a/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift +++ b/Source/Modules/UIModules/TooltipUI/ctlTooltip.swift @@ -40,7 +40,7 @@ public class ctlTooltip: NSWindowController { let panel = NSPanel( contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false ) - panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 2) panel.hasShadow = true panel.backgroundColor = NSColor.controlBackgroundColor messageText = NSAttributedTextView() diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index fed689fb..744b2dca 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; }; 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; }; 5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34527AE7CD900A19448 /* NotifierController.swift */; }; + 5B630A3C28CC97020010D076 /* ctlPopupCompositionBuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B630A3B28CC97020010D076 /* ctlPopupCompositionBuffer.swift */; }; 5B6C141228A9D4B30098ADF8 /* ctlInputMethod_Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */; }; 5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; }; 5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; }; @@ -253,6 +254,7 @@ 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NotifierController.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; + 5B630A3B28CC97020010D076 /* ctlPopupCompositionBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlPopupCompositionBuffer.swift; sourceTree = ""; }; 5B65B919284D0185007C558B /* README-CHT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHT.md"; sourceTree = ""; }; 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Common.swift; sourceTree = ""; }; 5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; }; @@ -581,6 +583,7 @@ children = ( 5B62A33E27AE7CD900A19448 /* CandidateUI */, 5B62A34427AE7CD900A19448 /* NotifierUI */, + 5B630A3A28CC96D80010D076 /* PopupCompositionBufferUI */, 5BA9FD0927FED9F3002DE248 /* PrefUI */, 5B62A34227AE7CD900A19448 /* TooltipUI */, ); @@ -645,6 +648,14 @@ name = Data; sourceTree = ""; }; + 5B630A3A28CC96D80010D076 /* PopupCompositionBufferUI */ = { + isa = PBXGroup; + children = ( + 5B630A3B28CC97020010D076 /* ctlPopupCompositionBuffer.swift */, + ); + path = PopupCompositionBufferUI; + sourceTree = ""; + }; 5B84579B2871AD2200C93B01 /* HotenkaChineseConverter */ = { isa = PBXGroup; children = ( @@ -1268,6 +1279,7 @@ 5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */, 5B2170E8289FACAD00BE7304 /* 5_Vertex.swift in Sources */, 5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */, + 5B630A3C28CC97020010D076 /* ctlPopupCompositionBuffer.swift in Sources */, 5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */, 5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */, 5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */, From fc0bfd0bbc8563bc149c9a55d073c45736eaa956 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 12:11:39 +0800 Subject: [PATCH 14/21] ctlIME // Disable alphanumerical notification on activateServer(). --- .../ControllerModules/ctlInputMethod_Core.swift | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift index 4834e277..2c2f6bdd 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Core.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Core.swift @@ -118,18 +118,7 @@ class ctlInputMethod: IMKInputController { keyHandler.clear() // 這句不要砍,因為後面 handle State.Empty() 不一定執行。 keyHandler.ensureParser() - if isASCIIMode { - if mgrPrefs.disableShiftTogglingAlphanumericalMode { - isASCIIMode = false - } else { - NotifierController.notify( - message: NSLocalizedString("Alphanumerical Mode", comment: "") + "\n" - + (isASCIIMode - ? NSLocalizedString("NotificationSwitchON", comment: "") - : NSLocalizedString("NotificationSwitchOFF", comment: "")) - ) - } - } + if isASCIIMode, mgrPrefs.disableShiftTogglingAlphanumericalMode { isASCIIMode = false } /// 必須加上下述條件,否則會在每次切換至輸入法本體的視窗(比如偏好設定視窗)時會卡死。 /// 這是很多 macOS 副廠輸入法的常見失誤之處。 From 99558c8609429dfd31494df081820ac7df4c3d73 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 13:21:38 +0800 Subject: [PATCH 15/21] AboutWindow, etc. // Maintenance. --- Installer/AppDelegate.swift | 1 + Installer/Resources/Base.lproj/MainMenu.xib | 4 ++-- Source/Modules/AppDelegate.swift | 1 + Source/WindowControllers/ctlAboutWindow.swift | 2 -- Source/WindowNIBs/Base.lproj/frmAboutWindow.xib | 4 ++-- UserPhraseEditor/AppDelegate.swift | 1 + UserPhraseEditor/Resources/Base.lproj/frmAboutWindow.xib | 4 ++-- UserPhraseEditor/ctlAboutWindow.swift | 2 -- 8 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Installer/AppDelegate.swift b/Installer/AppDelegate.swift index f9415522..f0dc6e74 100644 --- a/Installer/AppDelegate.swift +++ b/Installer/AppDelegate.swift @@ -101,6 +101,7 @@ class AppDelegate: NSWindowController, NSApplicationDelegate { window.standardWindowButton(.closeButton)?.isHidden = true window.standardWindowButton(.miniaturizeButton)?.isHidden = true window.standardWindowButton(.zoomButton)?.isHidden = true + window.titlebarAppearsTransparent = true if FileManager.default.fileExists( atPath: kTargetPartialPath) diff --git a/Installer/Resources/Base.lproj/MainMenu.xib b/Installer/Resources/Base.lproj/MainMenu.xib index f695105e..ba9f1014 100644 --- a/Installer/Resources/Base.lproj/MainMenu.xib +++ b/Installer/Resources/Base.lproj/MainMenu.xib @@ -198,7 +198,7 @@ DQ - + @@ -220,7 +220,7 @@ DQ - + DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database. diff --git a/Source/Modules/AppDelegate.swift b/Source/Modules/AppDelegate.swift index 154c0fa9..50a27f92 100644 --- a/Source/Modules/AppDelegate.swift +++ b/Source/Modules/AppDelegate.swift @@ -99,6 +99,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele ctlAboutWindowInstance?.window?.center() ctlAboutWindowInstance?.window?.orderFrontRegardless() // 逼著關於視窗往最前方顯示 ctlAboutWindowInstance?.window?.level = .statusBar + ctlAboutWindowInstance?.window?.titlebarAppearsTransparent = true NSApp.setActivationPolicy(.accessory) } diff --git a/Source/WindowControllers/ctlAboutWindow.swift b/Source/WindowControllers/ctlAboutWindow.swift index 78a445aa..8d043b06 100644 --- a/Source/WindowControllers/ctlAboutWindow.swift +++ b/Source/WindowControllers/ctlAboutWindow.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) diff --git a/Source/WindowNIBs/Base.lproj/frmAboutWindow.xib b/Source/WindowNIBs/Base.lproj/frmAboutWindow.xib index fa0e959e..a531c46a 100644 --- a/Source/WindowNIBs/Base.lproj/frmAboutWindow.xib +++ b/Source/WindowNIBs/Base.lproj/frmAboutWindow.xib @@ -55,7 +55,7 @@ - + @@ -77,7 +77,7 @@ - + DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database. diff --git a/UserPhraseEditor/AppDelegate.swift b/UserPhraseEditor/AppDelegate.swift index f023f836..d471b5b8 100644 --- a/UserPhraseEditor/AppDelegate.swift +++ b/UserPhraseEditor/AppDelegate.swift @@ -32,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { ctlAboutWindowInstance?.window?.center() ctlAboutWindowInstance?.window?.orderFrontRegardless() // 逼著關於視窗往最前方顯示 ctlAboutWindowInstance?.window?.level = .statusBar + ctlAboutWindowInstance?.window?.titlebarAppearsTransparent = true } // Call the New About Window diff --git a/UserPhraseEditor/Resources/Base.lproj/frmAboutWindow.xib b/UserPhraseEditor/Resources/Base.lproj/frmAboutWindow.xib index 48d3f54f..91fc6999 100644 --- a/UserPhraseEditor/Resources/Base.lproj/frmAboutWindow.xib +++ b/UserPhraseEditor/Resources/Base.lproj/frmAboutWindow.xib @@ -55,7 +55,7 @@ - + vChewing Phrase Editor developed by Shiki Suen. vChewing Phrase Database Maintained by Shiki Suen. @@ -79,7 +79,7 @@ vChewing Phrase Database Maintained by Shiki Suen. - + DISCLAIMER: The vChewing project, having no relationship of cooperation or affiliation with the OpenVanilla project, is not responsible for the phrase database shipped in the original McBopomofo project. Certain geopolitical and ideological contents, which are potentially harmful to the global spread of this software, are not included in vChewing official phrase database. diff --git a/UserPhraseEditor/ctlAboutWindow.swift b/UserPhraseEditor/ctlAboutWindow.swift index 0d520a1f..9f6ed90e 100644 --- a/UserPhraseEditor/ctlAboutWindow.swift +++ b/UserPhraseEditor/ctlAboutWindow.swift @@ -1,5 +1,3 @@ -// (c) 2011 and onwards The OpenVanilla Project (MIT License). -// All possible vChewing-specific modifications are of: // (c) 2021 and onwards The vChewing Project (MIT-NTL License). // ==================== // This code is released under the MIT license (SPDX-License-Identifier: MIT) From 3b905991488dfa0f4e72fc56934c0705b70faa70 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 16:17:57 +0800 Subject: [PATCH 16/21] UpdateSputnik // Remove a useless and misleading term. --- Source/Modules/IMEModules/UpdateSputnik.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Modules/IMEModules/UpdateSputnik.swift b/Source/Modules/IMEModules/UpdateSputnik.swift index dfaa4c05..9f028f63 100644 --- a/Source/Modules/IMEModules/UpdateSputnik.swift +++ b/Source/Modules/IMEModules/UpdateSputnik.swift @@ -152,7 +152,7 @@ class UpdateSputnik: NSObject, URLSessionDataDelegate { let content = NSLocalizedString(message, comment: "") alert.messageText = NSLocalizedString("Update Check Failed", comment: "") alert.informativeText = content - alert.addButton(withTitle: NSLocalizedString("Dismiss", comment: "")) + alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) alert.runModal() NSApp.setActivationPolicy(.accessory) } From 816d9fea1853657d7e4dff8e3fd3a0dab6bd3742 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 16:20:20 +0800 Subject: [PATCH 17/21] i18n // "Dismiss" -> "Cancel". --- Source/Resources/Base.lproj/Localizable.strings | 2 +- Source/Resources/en.lproj/Localizable.strings | 2 +- Source/Resources/ja.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hans.lproj/Localizable.strings | 2 +- Source/Resources/zh-Hant.lproj/Localizable.strings | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 286e1235..5c83469d 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Update Check Failed" = "Update Check Failed"; "There may be no internet connection or the server failed to respond.\n\nError message: %@" = "There may be no internet connection or the server failed to respond.\n\nError message: %@"; "OK" = "OK"; -"Dismiss" = "Dismiss"; +"Cancel" = "Cancel"; "New Version Available" = "New Version Available"; "Not Now" = "Not Now"; "Visit Website" = "Visit Website"; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 286e1235..5c83469d 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Update Check Failed" = "Update Check Failed"; "There may be no internet connection or the server failed to respond.\n\nError message: %@" = "There may be no internet connection or the server failed to respond.\n\nError message: %@"; "OK" = "OK"; -"Dismiss" = "Dismiss"; +"Cancel" = "Cancel"; "New Version Available" = "New Version Available"; "Not Now" = "Not Now"; "Visit Website" = "Visit Website"; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index f8aa5afb..a7cd23b8 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Update Check Failed" = "更新通知受信失敗"; "There may be no internet connection or the server failed to respond.\n\nError message: %@" = "ネットの接続の有無の問題か、サーバー側の反応の問題か。\n\nいずれにせよ、エラーメッセージ:「%@」"; "OK" = "うむ"; -"Dismiss" = "却下"; +"Cancel" = "取り消す"; "New Version Available" = "最新版利用可能"; "Not Now" = "後で"; "Visit Website" = "公式サイト"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index d5c0036e..bb23e1fd 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Update Check Failed" = "无法检查新版"; "There may be no internet connection or the server failed to respond.\n\nError message: %@" = "网路连线失败,或是伺服器没有回应。\n\n错误说明:%@"; "OK" = "确定"; -"Dismiss" = "关闭本视窗"; +"Cancel" = "取消"; "New Version Available" = "有新版可下载"; "Not Now" = "以后再说"; "Visit Website" = "前往网站"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index e279ade0..42538fd8 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Update Check Failed" = "無法檢查新版"; "There may be no internet connection or the server failed to respond.\n\nError message: %@" = "網路連線失敗,或是伺服器沒有回應。\n\n錯誤說明:%@"; "OK" = "確定"; -"Dismiss" = "關閉本視窗"; +"Cancel" = "取消"; "New Version Available" = "有新版可下載"; "Not Now" = "以後再說"; "Visit Website" = "前往網站"; From d21d0342bfc0894059973b9ecb2c4862b4eb01f5 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 18:32:15 +0800 Subject: [PATCH 18/21] Repo // Introduce ClientListMgr. --- Source/Modules/AppDelegate.swift | 12 ++ .../ctlInputMethod_Menu.swift | 11 ++ .../Resources/Base.lproj/Localizable.strings | 11 ++ Source/Resources/en.lproj/Localizable.strings | 11 ++ Source/Resources/ja.lproj/Localizable.strings | 11 ++ .../zh-Hans.lproj/Localizable.strings | 11 ++ .../zh-Hant.lproj/Localizable.strings | 11 ++ .../WindowControllers/ctlClientListMgr.swift | 167 ++++++++++++++++++ Source/WindowNIBs/frmClientListMgr.xib | 133 ++++++++++++++ vChewing.xcodeproj/project.pbxproj | 8 + 10 files changed, 386 insertions(+) create mode 100644 Source/WindowControllers/ctlClientListMgr.swift create mode 100644 Source/WindowNIBs/frmClientListMgr.xib diff --git a/Source/Modules/AppDelegate.swift b/Source/Modules/AppDelegate.swift index 50a27f92..19fce5a2 100644 --- a/Source/Modules/AppDelegate.swift +++ b/Source/Modules/AppDelegate.swift @@ -25,6 +25,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele // let vChewingKeyLayoutBundle = Bundle.init(path: URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("vChewingKeyLayout.bundle").path) @IBOutlet var window: NSWindow? + private var ctlClientListMgrInstance: ctlClientListMgr? private var ctlPrefWindowInstance: ctlPrefWindow? private var ctlAboutWindowInstance: ctlAboutWindow? // New About Window public lazy var folderMonitor = FolderMonitor( @@ -80,6 +81,17 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele folderMonitor.startMonitoring() } + func showClientListMgr() { + if ctlClientListMgrInstance == nil { + ctlClientListMgrInstance = ctlClientListMgr.init(windowNibName: "frmClientListMgr") + } + ctlClientListMgrInstance?.window?.center() + ctlClientListMgrInstance?.window?.orderFrontRegardless() // 逼著屬性視窗往最前方顯示 + ctlClientListMgrInstance?.window?.level = .statusBar + ctlClientListMgrInstance?.window?.titlebarAppearsTransparent = true + NSApp.setActivationPolicy(.accessory) + } + func showPreferences() { if ctlPrefWindowInstance == nil { ctlPrefWindowInstance = ctlPrefWindow.init(windowNibName: "frmPrefWindow") diff --git a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift index 31b35756..9cf870e3 100644 --- a/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift +++ b/Source/Modules/ControllerModules/ctlInputMethod_Menu.swift @@ -156,6 +156,12 @@ extension ctlInputMethod { withTitle: NSLocalizedString("vChewing Preferences…", comment: ""), action: #selector(showPreferences(_:)), keyEquivalent: "" ) + } + menu.addItem( + withTitle: NSLocalizedString("Client Manager", comment: "") + "…", + action: #selector(showClientListMgr(_:)), keyEquivalent: "" + ) + if !optionKeyPressed { menu.addItem( withTitle: NSLocalizedString("Check for Updates…", comment: ""), action: #selector(checkForUpdate(_:)), keyEquivalent: "" @@ -208,6 +214,11 @@ extension ctlInputMethod { NSWorkspace.shared.openFile(url.path, withApplication: "Safari") } + @objc func showClientListMgr(_: Any?) { + (NSApp.delegate as? AppDelegate)?.showClientListMgr() + NSApp.activate(ignoringOtherApps: true) + } + @objc func toggleSCPCTypingMode(_: Any? = nil) { resetKeyHandler() NotifierController.notify( diff --git a/Source/Resources/Base.lproj/Localizable.strings b/Source/Resources/Base.lproj/Localizable.strings index 5c83469d..a52613a2 100644 --- a/Source/Resources/Base.lproj/Localizable.strings +++ b/Source/Resources/Base.lproj/Localizable.strings @@ -1,4 +1,15 @@ "vChewing" = "vChewing"; +"Please enter the client app bundle identifier(s) you want to register." = "Please enter the client app bundle identifier(s) you want to register."; +"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed."; +"Just Select" = "Just Select"; +"Client Manager" = "Client Manager"; +"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable."; +"Add Client" = "Add Client"; +"Remove Selected" = "Remove Selected"; +"Choose the target application bundle." = "Choose the target application bundle."; +"The selected item is not a valid macOS application bundle." = "The selected item is not a valid macOS application bundle."; +"Please try again." = "Please try again."; +"The selected item's identifier is already in the list." = "The selected item's identifier is already in the list."; "Update Check Completed" = "Update Check Completed"; "You are already using the latest version." = "You are already using the latest version."; "Plist downloaded is nil." = "Plist downloaded is nil."; diff --git a/Source/Resources/en.lproj/Localizable.strings b/Source/Resources/en.lproj/Localizable.strings index 5c83469d..a52613a2 100644 --- a/Source/Resources/en.lproj/Localizable.strings +++ b/Source/Resources/en.lproj/Localizable.strings @@ -1,4 +1,15 @@ "vChewing" = "vChewing"; +"Please enter the client app bundle identifier(s) you want to register." = "Please enter the client app bundle identifier(s) you want to register."; +"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed."; +"Just Select" = "Just Select"; +"Client Manager" = "Client Manager"; +"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable."; +"Add Client" = "Add Client"; +"Remove Selected" = "Remove Selected"; +"Choose the target application bundle." = "Choose the target application bundle."; +"The selected item is not a valid macOS application bundle." = "The selected item is not a valid macOS application bundle."; +"Please try again." = "Please try again."; +"The selected item's identifier is already in the list." = "The selected item's identifier is already in the list."; "Update Check Completed" = "Update Check Completed"; "You are already using the latest version." = "You are already using the latest version."; "Plist downloaded is nil." = "Plist downloaded is nil."; diff --git a/Source/Resources/ja.lproj/Localizable.strings b/Source/Resources/ja.lproj/Localizable.strings index a7cd23b8..3e465824 100644 --- a/Source/Resources/ja.lproj/Localizable.strings +++ b/Source/Resources/ja.lproj/Localizable.strings @@ -1,4 +1,15 @@ "vChewing" = "威注音入力アプリ"; +"Please enter the client app bundle identifier(s) you want to register." = "登録したい客体アプリの唯一識別子(Bundle Identifier)をご入力ください。"; +"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "毎行は1つ記録とみなす。Option+Enter キーで改行。\n空白の記録値は無視される。"; +"Just Select" = "直接に選ぶ"; +"Client Manager" = "客体アプリの管理"; +"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "IMKTextInput 議定規約に従っていない客体アプリはここでご登録ください。登録済みのアプリは客体アプリ(文字入力を受くアプリ)とされた時に、威注音入力アプリは「吹き出し入力緩衝列ウィンドウ」と「緩衝列容量制限」を起用し、その容量制限は最大限音読み20箇とする。"; +"Add Client" = "入れる"; +"Remove Selected" = "外す"; +"Choose the target application bundle." = "登録したいアプリのバンドルのお選びを。"; +"The selected item is not a valid macOS application bundle." = "今選んだんのは正しい macOS アプリバンドルではないと考えられる。"; +"Please try again." = "お選び直しください。"; +"The selected item's identifier is already in the list." = "今選んだバンドルの唯一識別子(Bundle Identifier)は既に登録済みである。"; "Update Check Completed" = "新バージョンチェック完了"; "You are already using the latest version." = "現在稼働中のは最新バージョンである。"; "Plist downloaded is nil." = "受けた新バージョンお知らせ情報データは Plist ではないため、失敗とみなす。"; diff --git a/Source/Resources/zh-Hans.lproj/Localizable.strings b/Source/Resources/zh-Hans.lproj/Localizable.strings index bb23e1fd..e989b8d8 100644 --- a/Source/Resources/zh-Hans.lproj/Localizable.strings +++ b/Source/Resources/zh-Hans.lproj/Localizable.strings @@ -1,4 +1,15 @@ "vChewing" = "威注音输入法"; +"Please enter the client app bundle identifier(s) you want to register." = "请键入您要登记的客体应用的唯一标帜(Bundle Identifier)。"; +"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "每行一笔记录,用 Option+Enter 换行。\n空白值会被无视。"; +"Just Select" = "直接选取"; +"Client Manager" = "管理客体应用"; +"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "请在此管理那些不遵守 IMKTextInput 协定的客体应用。威注音输入法对于任何位列在此的客体应用均启用浮动组字窗、且对组字区内容设定容量上限(最多二十个读音)。"; +"Add Client" = "登记新客体"; +"Remove Selected" = "移除所选条目"; +"Choose the target application bundle." = "请选择要登记的应用程式的封包。"; +"The selected item is not a valid macOS application bundle." = "当前所选之物并非 macOS 应用程式封包。"; +"Please try again." = "请重试。"; +"The selected item's identifier is already in the list." = "当前所选之封包的唯一标帜(Bundle Identifier)已被登记。"; "Update Check Completed" = "更新检查完毕"; "You are already using the latest version." = "您正在使用目前最新的发行版。"; "Plist downloaded is nil." = "下载来的更新资讯并非 Plist 档案。"; diff --git a/Source/Resources/zh-Hant.lproj/Localizable.strings b/Source/Resources/zh-Hant.lproj/Localizable.strings index 42538fd8..2d686182 100644 --- a/Source/Resources/zh-Hant.lproj/Localizable.strings +++ b/Source/Resources/zh-Hant.lproj/Localizable.strings @@ -1,4 +1,15 @@ "vChewing" = "威注音輸入法"; +"Please enter the client app bundle identifier(s) you want to register." = "請鍵入您要登記的客體應用的唯一標幟(Bundle Identifier)。"; +"One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed." = "每行一筆記錄,用 Option+Enter 換行。\n空白值會被無視。"; +"Just Select" = "直接選取"; +"Client Manager" = "管理客體應用"; +"Please manage the list of IMKTextInput-incompatible clients here. Clients listed here will trigger vChewing's built-in popup composition buffer window with maximum 20 reading counts holdable." = "請在此管理那些不遵守 IMKTextInput 協定的客體應用。威注音輸入法對於任何位列在此的客體應用均啟用浮動組字窗、且對組字區內容設定容量上限(最多二十個讀音)。"; +"Add Client" = "登記新客體"; +"Remove Selected" = "移除所選條目"; +"Choose the target application bundle." = "請選擇要登記的應用程式的封包。"; +"The selected item is not a valid macOS application bundle." = "當前所選之物並非 macOS 應用程式封包。"; +"Please try again." = "請重試。"; +"The selected item's identifier is already in the list." = "當前所選之封包的唯一標幟(Bundle Identifier)已被登記。"; "Update Check Completed" = "更新檢查完畢"; "You are already using the latest version." = "您正在使用目前最新的發行版。"; "Plist downloaded is nil." = "下載來的更新資訊並非 Plist 檔案。"; diff --git a/Source/WindowControllers/ctlClientListMgr.swift b/Source/WindowControllers/ctlClientListMgr.swift new file mode 100644 index 00000000..8dc51cbc --- /dev/null +++ b/Source/WindowControllers/ctlClientListMgr.swift @@ -0,0 +1,167 @@ +// (c) 2021 and onwards The vChewing Project (MIT-NTL License). +// ==================== +// This code is released under the MIT license (SPDX-License-Identifier: MIT) +// ... with NTL restriction stating that: +// No trademark license is granted to use the trade names, trademarks, service +// marks, or product names of Contributor, except as required to fulfill notice +// requirements defined in MIT License. + +import Cocoa + +class ctlClientListMgr: NSWindowController, NSTableViewDelegate, NSTableViewDataSource { + @IBOutlet var tblClients: NSTableView! + @IBOutlet var btnRemoveClient: NSButton! + @IBOutlet var btnAddClient: NSButton! + @IBOutlet var lblClientMgrWindow: NSTextField! + override func windowDidLoad() { + super.windowDidLoad() + localize() + tblClients.delegate = self + tblClients.dataSource = self + tblClients.reloadData() + } +} + +// MARK: - Implementations + +extension ctlClientListMgr { + func numberOfRows(in _: NSTableView) -> Int { + mgrPrefs.clientsIMKTextInputIncapable.count + } + + func callAlert(_ window: NSWindow, title: String, text: String? = nil) { + let alert = NSAlert() + alert.messageText = title + if let text = text { + alert.informativeText = text + } + alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + alert.beginSheetModal(for: window) + } + + @IBAction func btnAddClientClicked(_: Any) { + guard let window = window else { return } + let alert = NSAlert() + alert.messageText = NSLocalizedString( + "Please enter the client app bundle identifier(s) you want to register.", comment: "" + ) + alert.informativeText = NSLocalizedString( + "One record per line. Use Option+Enter to break lines.\nBlank lines will be dismissed.", comment: "" + ) + alert.addButton(withTitle: NSLocalizedString("OK", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Just Select", comment: "") + "…") + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + let maxFloat = CGFloat(Float.greatestFiniteMagnitude) + let scrollview = NSScrollView(frame: NSRect(x: 0, y: 0, width: 370, height: 380)) + let contentSize = scrollview.contentSize + scrollview.borderType = .noBorder + scrollview.hasVerticalScroller = true + scrollview.hasHorizontalScroller = true + scrollview.horizontalScroller?.scrollerStyle = .legacy + scrollview.verticalScroller?.scrollerStyle = .legacy + scrollview.autoresizingMask = [.width, .height] + let theTextView = NSTextView(frame: NSRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height)) + theTextView.minSize = NSSize(width: 0.0, height: contentSize.height) + theTextView.maxSize = NSSize(width: maxFloat, height: maxFloat) + theTextView.isVerticallyResizable = true + theTextView.isHorizontallyResizable = false + theTextView.autoresizingMask = .width + theTextView.textContainer?.containerSize = NSSize(width: contentSize.width, height: maxFloat) + theTextView.textContainer?.widthTracksTextView = true + scrollview.documentView = theTextView + theTextView.enclosingScrollView?.hasHorizontalScroller = true + theTextView.isHorizontallyResizable = true + theTextView.autoresizingMask = [.width, .height] + theTextView.textContainer?.containerSize = NSSize(width: maxFloat, height: maxFloat) + theTextView.textContainer?.widthTracksTextView = false + + alert.accessoryView = scrollview + alert.beginSheetModal(for: window) { result in + switch result { + case .alertFirstButtonReturn: + theTextView.textContainer?.textView?.string.components(separatedBy: "\n").filter { !$0.isEmpty }.forEach { + self.applyNewValue($0) + } + case .alertSecondButtonReturn: + IME.dlgOpenPath.title = NSLocalizedString( + "Choose the target application bundle.", comment: "" + ) + IME.dlgOpenPath.showsResizeIndicator = true + IME.dlgOpenPath.showsHiddenFiles = true + IME.dlgOpenPath.canChooseFiles = true + IME.dlgOpenPath.canChooseDirectories = false + IME.dlgOpenPath.beginSheetModal(for: window) { result in + switch result { + case .OK: + guard let url = IME.dlgOpenPath.url else { return } + var title = NSLocalizedString("The selected item is not a valid macOS application bundle.", comment: "") + let text = NSLocalizedString("Please try again.", comment: "") + guard let bundle = Bundle(url: url) else { + self.callAlert(window, title: title, text: text) + return + } + guard let identifier = bundle.bundleIdentifier else { + self.callAlert(window, title: title, text: text) + return + } + if mgrPrefs.clientsIMKTextInputIncapable.contains(identifier) { + title = NSLocalizedString( + "The selected item's identifier is already in the list.", comment: "" + ) + self.callAlert(window, title: title) + return + } + self.applyNewValue(identifier) + default: return + } + } + default: return + } + } + } + + private func applyNewValue(_ newValue: String) { + guard !newValue.isEmpty else { return } + var arrResult = mgrPrefs.clientsIMKTextInputIncapable + arrResult.append(newValue) + mgrPrefs.clientsIMKTextInputIncapable = arrResult.sorted() + tblClients.reloadData() + btnRemoveClient.isEnabled = (0..= mgrPrefs.clientsIMKTextInputIncapable.count { return } + if tblClients.selectedRow < 0 { return } + let isLastRow: Bool = { + if mgrPrefs.clientsIMKTextInputIncapable.count < 2 { return false } + return tblClients.selectedRow == mgrPrefs.clientsIMKTextInputIncapable.count - 1 + }() + mgrPrefs.clientsIMKTextInputIncapable.remove(at: tblClients.selectedRow) + if isLastRow { + tblClients.selectRowIndexes(.init(arrayLiteral: tblClients.selectedRow - 1), byExtendingSelection: false) + } + tblClients.reloadData() + btnRemoveClient.isEnabled = (0.. Any? { + defer { + self.btnRemoveClient.isEnabled = (0.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 744b2dca..32de0cc3 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 5B09307628B6FC3B0021F8C5 /* shortcuts.html in Resources */ = {isa = PBXBuildFile; fileRef = 5B09307828B6FC3B0021F8C5 /* shortcuts.html */; }; 5B0AF8B527B2C8290096FE54 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */; }; + 5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */; }; + 5B0EF55F28CDBF8E00F8F7CE /* ctlClientListMgr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0EF55E28CDBF8E00F8F7CE /* ctlClientListMgr.swift */; }; 5B11328927B94CFB00E58451 /* AppleKeyboardConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */; }; 5B16B84C28C9A89000ABA692 /* FolderMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */; }; 5B175FFB28C5CDDC0078D1B4 /* IMKHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */; }; @@ -211,6 +213,8 @@ 5B09307B28B6FC410021F8C5 /* ja */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; lineEnding = 0; name = ja; path = ja.lproj/shortcuts.html; sourceTree = ""; }; 5B0AF8B427B2C8290096FE54 /* StringExtension.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = StringExtension.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B0C5EDF27C7D9870078037C /* dataCompiler.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = dataCompiler.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; + 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = frmClientListMgr.xib; sourceTree = ""; }; + 5B0EF55E28CDBF8E00F8F7CE /* ctlClientListMgr.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ctlClientListMgr.swift; sourceTree = ""; }; 5B11328827B94CFB00E58451 /* AppleKeyboardConverter.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = AppleKeyboardConverter.swift; sourceTree = ""; tabWidth = 2; usesTabs = 0; }; 5B16B84B28C9A89000ABA692 /* FolderMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FolderMonitor.swift; sourceTree = ""; }; 5B175FFA28C5CDDC0078D1B4 /* IMKHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IMKHelper.swift; sourceTree = ""; }; @@ -594,6 +598,7 @@ isa = PBXGroup; children = ( 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */, + 5B0EF55E28CDBF8E00F8F7CE /* ctlClientListMgr.swift */, D47F7DCD278BFB57002F9DD7 /* ctlPrefWindow.swift */, ); path = WindowControllers; @@ -602,6 +607,7 @@ 5B62A33B27AE7C7F00A19448 /* WindowNIBs */ = { isa = PBXGroup; children = ( + 5B0EF55C28CDBF7100F8F7CE /* frmClientListMgr.xib */, 5BBBB76927AED5DB0023B93A /* frmAboutWindow.xib */, 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */, 6A187E2816004C5900466B2E /* MainMenu.xib */, @@ -1079,6 +1085,7 @@ D4E33D8A27A838CF006DB1CF /* Localizable.strings in Resources */, 5BF9DA2828840E6200DBD48E /* template-exclusions.txt in Resources */, 5B7DA80328BF6BC600D7B2AD /* fixinstall.sh in Resources */, + 5B0EF55D28CDBF7100F8F7CE /* frmClientListMgr.xib in Resources */, 5BDCBB2E27B4E67A00D0CC59 /* vChewingPhraseEditor.app in Resources */, 5BBBB76027AED54C0023B93A /* Fart.m4a in Resources */, 6A2E40F6253A69DA00D1AE1D /* Images.xcassets in Resources */, @@ -1225,6 +1232,7 @@ 5B5F8AEE28C8826D007C11F1 /* ctlTooltip.swift in Sources */, 5BD0113B28180D6100609769 /* LMInstantiator.swift in Sources */, 5B2170E7289FACAD00BE7304 /* 1_Compositor.swift in Sources */, + 5B0EF55F28CDBF8E00F8F7CE /* ctlClientListMgr.swift in Sources */, 5B21177028753B9D000443A9 /* ctlInputMethod_Delegates.swift in Sources */, 5B21176E28753B35000443A9 /* ctlInputMethod_HandleDisplay.swift in Sources */, 5B84579F2871AD2200C93B01 /* HotenkaChineseConverter.swift in Sources */, From c7ac1d7518a588896d196a83c47ccb6f399dbc55 Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 19:33:07 +0800 Subject: [PATCH 19/21] AUTHORS // Remove outdated credits. --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index b6d8fe4b..fac1bdbb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,7 +17,7 @@ $ Contributors and volunteers of the upstream repo, having no responsibility in - Zonble Yang: - McBopomofo for macOS 2.x architect. - Voltaire candidate window MK2 (massively modified as MK3 in vChewing by Shiki Suen). - - Notifier window and Tooltip UI. + - Notifier window. - App-style installer (only preserved for developer purposes). - mgrPrefs (userdefaults manager). - Mengjuei Hsieh: From 2f39b93f5bcc7c9c06312e76409864af510bd9ce Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 11:59:02 +0800 Subject: [PATCH 20/21] Update Data - 20220911 --- Source/Data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Data b/Source/Data index 6dba7237..eeff2e3f 160000 --- a/Source/Data +++ b/Source/Data @@ -1 +1 @@ -Subproject commit 6dba7237957681bfa4c623ab5689248cc02bc7e6 +Subproject commit eeff2e3f7073873c07b4011717e32430db01227f From 1c43d6b3bcc4a6dd1e59caed08dafc7d7dd57f4c Mon Sep 17 00:00:00 2001 From: ShikiSuen Date: Sun, 11 Sep 2022 19:12:21 +0800 Subject: [PATCH 21/21] Bump version to 2.6.0 Build 2600. --- Update-Info.plist | 4 ++-- vChewing.pkgproj | 2 +- vChewing.xcodeproj/project.pbxproj | 32 +++++++++++++++--------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Update-Info.plist b/Update-Info.plist index 2137161c..544ec404 100644 --- a/Update-Info.plist +++ b/Update-Info.plist @@ -3,9 +3,9 @@ CFBundleShortVersionString - 2.5.0 + 2.6.0 CFBundleVersion - 2500 + 2600 UpdateInfoEndpoint https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist UpdateInfoSite diff --git a/vChewing.pkgproj b/vChewing.pkgproj index e4a6358e..3672605f 100644 --- a/vChewing.pkgproj +++ b/vChewing.pkgproj @@ -726,7 +726,7 @@ USE_HFS+_COMPRESSION VERSION - 2.5.0 + 2.6.0 TYPE 0 diff --git a/vChewing.xcodeproj/project.pbxproj b/vChewing.xcodeproj/project.pbxproj index 32de0cc3..6ac32cb2 100644 --- a/vChewing.xcodeproj/project.pbxproj +++ b/vChewing.xcodeproj/project.pbxproj @@ -1483,7 +1483,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -1493,7 +1493,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1522,13 +1522,13 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; ENABLE_NS_ASSERTIONS = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewingTests; @@ -1560,7 +1560,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; ENABLE_HARDENED_RUNTIME = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1582,7 +1582,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1612,7 +1612,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; @@ -1630,7 +1630,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingPhraseEditor; @@ -1746,7 +1746,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1775,7 +1775,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1805,7 +1805,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = ""; @@ -1828,7 +1828,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.inputmethod.vChewing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1852,7 +1852,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; @@ -1873,7 +1873,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1896,7 +1896,7 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2500; + CURRENT_PROJECT_VERSION = 2600; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = ""; ENABLE_HARDENED_RUNTIME = YES; @@ -1911,7 +1911,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.5.0; + MARKETING_VERSION = 2.6.0; PRODUCT_BUNDLE_IDENTIFIER = org.atelierInmu.vChewing.vChewingInstaller; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "";