Repo // Add ctlPopupCompositionBuffer.
This commit is contained in:
parent
fc976a6363
commit
d98b2bbc34
|
@ -27,9 +27,12 @@ class ctlInputMethod: IMKInputController {
|
||||||
static var ctlCandidateCurrent: ctlCandidateProtocol =
|
static var ctlCandidateCurrent: ctlCandidateProtocol =
|
||||||
mgrPrefs.useIMKCandidateWindow ? ctlCandidateIMK.init(.horizontal) : ctlCandidateUniversal.init(.horizontal)
|
mgrPrefs.useIMKCandidateWindow ? ctlCandidateIMK.init(.horizontal) : ctlCandidateUniversal.init(.horizontal)
|
||||||
|
|
||||||
/// 工具提示視窗的副本,每次都重新初始化。
|
/// 工具提示視窗的共用副本。
|
||||||
static var tooltipInstance = ctlTooltip()
|
static var tooltipInstance = ctlTooltip()
|
||||||
|
|
||||||
|
/// 浮動組字窗的共用副本。
|
||||||
|
static var popupCompositionBuffer = ctlPopupCompositionBuffer()
|
||||||
|
|
||||||
// MARK: -
|
// MARK: -
|
||||||
|
|
||||||
/// 當前這個 ctlInputMethod 副本是否處於英數輸入模式(滯後項)。
|
/// 當前這個 ctlInputMethod 副本是否處於英數輸入模式(滯後項)。
|
||||||
|
|
|
@ -67,7 +67,6 @@ extension ctlInputMethod {
|
||||||
if !state.tooltip.isEmpty {
|
if !state.tooltip.isEmpty {
|
||||||
show(tooltip: state.tooltip)
|
show(tooltip: state.tooltip)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
case .ofMarking:
|
case .ofMarking:
|
||||||
ctlInputMethod.ctlCandidateCurrent.visible = false
|
ctlInputMethod.ctlCandidateCurrent.visible = false
|
||||||
setInlineDisplayWithCursor()
|
setInlineDisplayWithCursor()
|
||||||
|
@ -82,6 +81,14 @@ extension ctlInputMethod {
|
||||||
show(candidateWindowWith: state)
|
show(candidateWindowWith: state)
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
|
// 浮動組字窗的顯示判定
|
||||||
|
if state.hasComposition, mgrPrefs.clientsIMKTextInputIncapable.contains(clientBundleIdentifier) {
|
||||||
|
ctlInputMethod.popupCompositionBuffer.show(
|
||||||
|
state: state, at: lineHeightRect(zeroCursor: true).origin
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ctlInputMethod.popupCompositionBuffer.hide()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 針對受 .NotEmpty() 管轄的非空狀態,在組字區內顯示游標。
|
/// 針對受 .NotEmpty() 管轄的非空狀態,在組字區內顯示游標。
|
||||||
|
|
|
@ -346,7 +346,7 @@ public class ctlCandidateUniversal: ctlCandidate {
|
||||||
let panel = NSPanel(
|
let panel = NSPanel(
|
||||||
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
|
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.hasShadow = true
|
||||||
panel.isOpaque = false
|
panel.isOpaque = false
|
||||||
panel.backgroundColor = NSColor.clear
|
panel.backgroundColor = NSColor.clear
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ public class ctlTooltip: NSWindowController {
|
||||||
let panel = NSPanel(
|
let panel = NSPanel(
|
||||||
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
|
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.hasShadow = true
|
||||||
panel.backgroundColor = NSColor.controlBackgroundColor
|
panel.backgroundColor = NSColor.controlBackgroundColor
|
||||||
messageText = NSAttributedTextView()
|
messageText = NSAttributedTextView()
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
5B62A33D27AE7CC100A19448 /* ctlAboutWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */; };
|
||||||
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
|
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34027AE7CD900A19448 /* ctlCandidate.swift */; };
|
||||||
5B62A34A27AE7CD900A19448 /* NotifierController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B62A34527AE7CD900A19448 /* NotifierController.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 */; };
|
5B6C141228A9D4B30098ADF8 /* ctlInputMethod_Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */; };
|
||||||
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
||||||
5B782EC4280C243C007276DE /* KeyHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* KeyHandler_HandleCandidate.swift */; };
|
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 = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B62A33C27AE7CC100A19448 /* ctlAboutWindow.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlAboutWindow.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B62A34027AE7CD900A19448 /* ctlCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = ctlCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NotifierController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B62A34527AE7CD900A19448 /* NotifierController.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = NotifierController.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
|
5B630A3B28CC97020010D076 /* ctlPopupCompositionBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlPopupCompositionBuffer.swift; sourceTree = "<group>"; };
|
||||||
5B65B919284D0185007C558B /* README-CHT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHT.md"; sourceTree = "<group>"; };
|
5B65B919284D0185007C558B /* README-CHT.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "README-CHT.md"; sourceTree = "<group>"; };
|
||||||
5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Common.swift; sourceTree = "<group>"; };
|
5B6C141128A9D4B30098ADF8 /* ctlInputMethod_Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ctlInputMethod_Common.swift; sourceTree = "<group>"; };
|
||||||
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
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 = (
|
children = (
|
||||||
5B62A33E27AE7CD900A19448 /* CandidateUI */,
|
5B62A33E27AE7CD900A19448 /* CandidateUI */,
|
||||||
5B62A34427AE7CD900A19448 /* NotifierUI */,
|
5B62A34427AE7CD900A19448 /* NotifierUI */,
|
||||||
|
5B630A3A28CC96D80010D076 /* PopupCompositionBufferUI */,
|
||||||
5BA9FD0927FED9F3002DE248 /* PrefUI */,
|
5BA9FD0927FED9F3002DE248 /* PrefUI */,
|
||||||
5B62A34227AE7CD900A19448 /* TooltipUI */,
|
5B62A34227AE7CD900A19448 /* TooltipUI */,
|
||||||
);
|
);
|
||||||
|
@ -645,6 +648,14 @@
|
||||||
name = Data;
|
name = Data;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
5B630A3A28CC96D80010D076 /* PopupCompositionBufferUI */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
5B630A3B28CC97020010D076 /* ctlPopupCompositionBuffer.swift */,
|
||||||
|
);
|
||||||
|
path = PopupCompositionBufferUI;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
5B84579B2871AD2200C93B01 /* HotenkaChineseConverter */ = {
|
5B84579B2871AD2200C93B01 /* HotenkaChineseConverter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1268,6 +1279,7 @@
|
||||||
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
5BA9FD1327FEDB6B002DE248 /* suiPrefPaneDictionary.swift in Sources */,
|
||||||
5B2170E8289FACAD00BE7304 /* 5_Vertex.swift in Sources */,
|
5B2170E8289FACAD00BE7304 /* 5_Vertex.swift in Sources */,
|
||||||
5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */,
|
5BBBB77A27AEDC690023B93A /* clsSFX.swift in Sources */,
|
||||||
|
5B630A3C28CC97020010D076 /* ctlPopupCompositionBuffer.swift in Sources */,
|
||||||
5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */,
|
5BA9FD4727FEF3C9002DE248 /* PreferencesStyleController.swift in Sources */,
|
||||||
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */,
|
5B949BDB2816DDBC00D87B5D /* LMConsolidator.swift in Sources */,
|
||||||
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */,
|
5BFDF011289635C100417BBC /* ctlCandidateIMK.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue