Repo // Add ctlPopupCompositionBuffer.

This commit is contained in:
ShikiSuen 2022-09-10 19:38:49 +08:00
parent fc976a6363
commit d98b2bbc34
6 changed files with 153 additions and 4 deletions

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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)
}
}

View File

@ -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()

View File

@ -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 */,