diff --git a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift index 3876030b..eeaa6a3f 100644 --- a/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift +++ b/Packages/vChewing_CandidateWindow/Sources/CandidateWindow/CtlCandidate.swift @@ -52,7 +52,7 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { } set { DispatchQueue.main.async { - self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0) + self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0, useGCD: true) } } } @@ -72,37 +72,6 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol { visible = false } - /// 設定選字窗的顯示位置。 - /// - /// 需注意:該函數會藉由設定選字窗左上角頂點的方式、使選字窗始終位於某個螢幕之內。 - /// - /// - Parameters: - /// - windowTopLeftPoint: 給定的視窗顯示位置。 - /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 - public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) { - DispatchQueue.main.async { [self] in - guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return } - let windowSize = window.frame.size - - var adjustedPoint = windowTopLeftPoint - var delta = heightDelta - for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) { - screenFrame = frame - break - } - - if delta > screenFrame.size.height / 2.0 { delta = 0.0 } - - if adjustedPoint.y < screenFrame.minY + windowSize.height { - adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta - } - adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0) - adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0) - - window.setFrameTopLeftPoint(adjustedPoint) - } - } - // MARK: - 不需要在這裡仔細實作的內容。 @available(*, unavailable) diff --git a/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift new file mode 100644 index 00000000..703b40fe --- /dev/null +++ b/Packages/vChewing_CocoaExtension/Sources/CocoaExtension/CocoaExtension_NSWindowController.swift @@ -0,0 +1,81 @@ +// (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 +import InputMethodKit + +extension NSWindowController { + /// 設定選字窗的顯示位置。 + /// + /// 需注意:該函式會藉由設定選字窗左上角頂點的方式、使選字窗始終位於某個螢幕之內。 + /// + /// - Parameters: + /// - windowTopLeftPoint: 給定的視窗顯示位置。 + /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 + public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { + func doSet() { + guard let window = window, var screenFrame = NSScreen.main?.visibleFrame else { return } + let windowSize = window.frame.size + + var adjustedPoint = windowTopLeftPoint + var delta = heightDelta + for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) { + screenFrame = frame + break + } + + if delta > screenFrame.size.height / 2.0 { delta = 0.0 } + + if adjustedPoint.y < screenFrame.minY + windowSize.height { + adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta + } + adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0) + adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0) + + window.setFrameTopLeftPoint(adjustedPoint) + } + + if !useGCD { doSet() } else { DispatchQueue.main.async { doSet() } } + } +} + +extension IMKCandidates { + /// 設定選字窗的顯示位置。 + /// + /// 需注意:該函式會藉由設定選字窗左上角頂點的方式、使選字窗始終位於某個螢幕之內。 + /// + /// - Parameters: + /// - windowTopLeftPoint: 給定的視窗顯示位置。 + /// - heightDelta: 為了「防止選字窗抻出螢幕下方」而給定的預留高度。 + public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double, useGCD: Bool) { + func doSet() { + DispatchQueue.main.async { [self] in + guard var screenFrame = NSScreen.main?.visibleFrame else { return } + var adjustedPoint = windowTopLeftPoint + let windowSize = candidateFrame().size + var delta = heightDelta + for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) { + screenFrame = frame + break + } + + if delta > screenFrame.size.height / 2.0 { delta = 0.0 } + + if adjustedPoint.y < screenFrame.minY + windowSize.height { + adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta + } + adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0) + adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0) + + setCandidateFrameTopLeft(adjustedPoint) + } + } + + if useGCD { doSet() } else { DispatchQueue.main.async { doSet() } } + } +} diff --git a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift index 1a013e82..ae98b032 100644 --- a/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift +++ b/Packages/vChewing_Shared/Sources/Shared/Protocols/CtlCandidateProtocol.swift @@ -36,5 +36,5 @@ public protocol CtlCandidateProtocol { func highlightNextCandidate() -> Bool func highlightPreviousCandidate() -> Bool func candidateIndexAtKeyLabelIndex(_: Int) -> Int - func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: Double) + func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: Double, useGCD: Bool) } diff --git a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift index af044ce6..ff85d358 100644 --- a/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift +++ b/Packages/vChewing_TooltipUI/Sources/TooltipUI/TooltipUI.swift @@ -57,8 +57,10 @@ public class TooltipUI: NSWindowController { ) { self.direction = direction self.tooltip = tooltip + window?.setIsVisible(false) window?.orderFront(nil) - set(windowTopLeftPoint: point, bottomOutOfScreenAdjustmentHeight: heightDelta) + set(windowTopLeftPoint: point, bottomOutOfScreenAdjustmentHeight: heightDelta, useGCD: false) + window?.setIsVisible(true) } public func setColor(state: TooltipColorState) { @@ -118,29 +120,6 @@ public class TooltipUI: NSWindowController { window?.orderOut(nil) } - private func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) { - guard let window = window else { return } - let windowSize = window.frame.size - - var adjustedPoint = windowTopLeftPoint - var delta = heightDelta - var screenFrame = NSScreen.main?.visibleFrame ?? NSRect.seniorTheBeast - for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) { - screenFrame = frame - break - } - - if delta > screenFrame.size.height / 2.0 { delta = 0.0 } - - if adjustedPoint.y < screenFrame.minY + windowSize.height { - adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta - } - adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0) - adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0) - - window.setFrameTopLeftPoint(adjustedPoint) - } - private func adjustSize() { var rect = messageText.shrinkFrame() var bigRect = rect diff --git a/Source/Modules/SessionCtl_HandleDisplay.swift b/Source/Modules/SessionCtl_HandleDisplay.swift index aaebb12a..bec330ee 100644 --- a/Source/Modules/SessionCtl_HandleDisplay.swift +++ b/Source/Modules/SessionCtl_HandleDisplay.swift @@ -133,12 +133,14 @@ extension SessionCtl { windowTopLeftPoint: NSPoint( 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, + useGCD: true ) } else { ctlCandidateCurrent.set( windowTopLeftPoint: NSPoint(x: lineHeightRect().origin.x, y: lineHeightRect().origin.y - 4.0), - bottomOutOfScreenAdjustmentHeight: lineHeightRect().size.height + 4.0 + bottomOutOfScreenAdjustmentHeight: lineHeightRect().size.height + 4.0, + useGCD: true ) } } diff --git a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift index 564b7191..694a438a 100644 --- a/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift +++ b/Source/Modules/UIModules/CandidateUI/IMKCandidatesImpl.swift @@ -36,7 +36,7 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { } set { DispatchQueue.main.async { - self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0) + self.set(windowTopLeftPoint: newValue, bottomOutOfScreenAdjustmentHeight: 0, useGCD: true) } } } @@ -146,33 +146,6 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol { get { selectedCandidate() } set { selectCandidate(withIdentifier: newValue) } } - - public func set(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight height: Double) { - DispatchQueue.main.async { - self.doSet(windowTopLeftPoint: windowTopLeftPoint, bottomOutOfScreenAdjustmentHeight: height) - } - } - - func doSet(windowTopLeftPoint: NSPoint, bottomOutOfScreenAdjustmentHeight heightDelta: Double) { - guard var screenFrame = NSScreen.main?.visibleFrame else { return } - var adjustedPoint = windowTopLeftPoint - let windowSize = candidateFrame().size - var delta = heightDelta - for frame in NSScreen.screens.map(\.visibleFrame).filter({ $0.contains(windowTopLeftPoint) }) { - screenFrame = frame - break - } - - if delta > screenFrame.size.height / 2.0 { delta = 0.0 } - - if adjustedPoint.y < screenFrame.minY + windowSize.height { - adjustedPoint.y = windowTopLeftPoint.y + windowSize.height + delta - } - adjustedPoint.y = min(adjustedPoint.y, screenFrame.maxY - 1.0) - adjustedPoint.x = min(max(adjustedPoint.x, screenFrame.minX), screenFrame.maxX - windowSize.width - 1.0) - - setCandidateFrameTopLeft(adjustedPoint) - } } // MARK: - Generate TISInputSource Object