2.8.0 SP2 // Vertical TDKCandidates, etc. Merge PR #154 from upd/2.8.0sp2

This commit is contained in:
ShikiSuen 2022-09-30 13:32:07 +08:00 committed by GitHub
commit 4257bcace0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 700 additions and 371 deletions

View File

@ -0,0 +1,37 @@
// (c) 2022 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 Shared
import SwiftUI
@available(macOS 12, *)
extension CandidateCellData {
public var attributedStringForSwiftUI: some View {
var result: some View {
ZStack(alignment: .leading) {
if isSelected {
Color(nsColor: CandidateCellData.highlightBackground).ignoresSafeArea().cornerRadius(6)
}
VStack(spacing: 0) {
HStack(spacing: 4) {
if UserDefaults.standard.bool(forKey: UserDef.kHandleDefaultCandidateFontsByLangIdentifier.rawValue) {
Text(AttributedString(attributedStringHeader)).frame(width: CandidateCellData.unifiedSize / 2)
Text(AttributedString(attributedString))
} else {
Text(key).font(.system(size: fontSizeKey).monospaced())
.foregroundColor(.init(nsColor: fontColorKey)).lineLimit(1)
Text(displayedText).font(.system(size: fontSizeCandidate))
.foregroundColor(.init(nsColor: fontColorCandidate)).lineLimit(1)
}
}.padding(4)
}
}.fixedSize(horizontal: false, vertical: true)
}
return result
}
}

View File

@ -12,53 +12,105 @@ import Shared
///
public class CandidatePool {
public let blankCell = CandidateCellData(key: " ", displayedText: " ", isSelected: false)
public var currentRowNumber = 0
public var maximumLinesPerPage = 3
public private(set) var candidateDataAll: [CandidateCellData] = []
public private(set) var selectionKeys: String
public private(set) var highlightedIndex: Int = 0
public private(set) var maxColumnCapacity: Int = 6
public private(set) var candidateDataAll: [CandidateCellData] = []
//
public var currentRowNumber = 0
public var maximumRowsPerPage = 3
public private(set) var maxRowCapacity: Int = 6
public private(set) var candidateRows: [[CandidateCellData]] = []
public var isVerticalLayout: Bool { maxColumnCapacity == 1 }
public var maxColumnWidth: Int { Int(Double(maxColumnCapacity + 3) * 2) * Int(ceil(CandidateCellData.unifiedSize)) }
//
public var currentColumnNumber = 0
public var maximumColumnsPerPage = 3
public private(set) var maxColumnCapacity: Int = 6
public private(set) var candidateColumns: [[CandidateCellData]] = []
//
public var maxRowWidth: Int { Int(Double(maxRowCapacity + 3) * 2) * Int(ceil(CandidateCellData.unifiedSize)) }
public var maxWindowWidth: Double {
ceil(Double(maxColumnCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2)
ceil(Double(maxRowCapacity + 3) * 2.7 * ceil(CandidateCellData.unifiedSize) * 1.2)
}
public var rangeForCurrentPage: Range<Int> {
currentRowNumber..<min(candidateRows.count, currentRowNumber + maximumLinesPerPage)
public var rangeForCurrentHorizontalPage: Range<Int> {
currentRowNumber..<min(candidateRows.count, currentRowNumber + maximumRowsPerPage)
}
public var rangeForLastPageBlanked: Range<Int> { 0..<(maximumLinesPerPage - rangeForCurrentPage.count) }
public var rangeForCurrentVerticalPage: Range<Int> {
currentColumnNumber..<min(candidateColumns.count, currentColumnNumber + maximumColumnsPerPage)
}
public var rangeForLastHorizontalPageBlanked: Range<Int> {
0..<(maximumRowsPerPage - rangeForCurrentHorizontalPage.count)
}
public var rangeForLastVerticalPageBlanked: Range<Int> {
0..<(maximumColumnsPerPage - rangeForCurrentVerticalPage.count)
}
public enum VerticalDirection {
case up
case down
}
///
public enum HorizontalDirection {
case left
case right
}
///
/// - Parameters:
/// - candidates:
/// - columnCapacity: (, )
public init(candidates: [String], columnCapacity: Int = 6, selectionKeys: String = "123456789", locale: String = "") {
/// - columnCapacity: (, )
/// - selectionKeys:
/// - locale: zh-Hanszh-Hant
public init(candidates: [String], columnCapacity: Int, selectionKeys: String = "123456789", locale: String = "") {
maxColumnCapacity = max(1, columnCapacity)
self.selectionKeys = selectionKeys
candidateDataAll = candidates.map { .init(key: "0", displayedText: $0) }
var currentColumn: [CandidateCellData] = []
for (i, candidate) in candidateDataAll.enumerated() {
candidate.index = i
candidate.whichRow = candidateRows.count
let isOverflown: Bool = currentColumn.map(\.cellLength).reduce(0, +) + candidate.cellLength > maxColumnWidth
if isOverflown || currentColumn.count == maxColumnCapacity, !currentColumn.isEmpty {
candidateRows.append(currentColumn)
candidate.whichColumn = candidateColumns.count
if currentColumn.count == maxColumnCapacity, !currentColumn.isEmpty {
candidateColumns.append(currentColumn)
currentColumn.removeAll()
candidate.whichRow += 1
candidate.whichColumn += 1
}
candidate.subIndex = currentColumn.count
candidate.locale = locale
currentColumn.append(candidate)
}
candidateRows.append(currentColumn)
candidateColumns.append(currentColumn)
}
///
/// - Parameters:
/// - candidates:
/// - rowCapacity: (, )
/// - selectionKeys:
/// - locale: zh-Hanszh-Hant
public init(candidates: [String], rowCapacity: Int, selectionKeys: String = "123456789", locale: String = "") {
maxRowCapacity = max(1, rowCapacity)
self.selectionKeys = selectionKeys
candidateDataAll = candidates.map { .init(key: "0", displayedText: $0) }
var currentRow: [CandidateCellData] = []
for (i, candidate) in candidateDataAll.enumerated() {
candidate.index = i
candidate.whichRow = candidateRows.count
let isOverflown: Bool = currentRow.map(\.cellLength).reduce(0, +) + candidate.cellLength > maxRowWidth
if isOverflown || currentRow.count == maxRowCapacity, !currentRow.isEmpty {
candidateRows.append(currentRow)
currentRow.removeAll()
candidate.whichRow += 1
}
candidate.subIndex = currentRow.count
candidate.locale = locale
currentRow.append(candidate)
}
candidateRows.append(currentRow)
}
public func selectNewNeighborRow(direction: VerticalDirection) {
@ -70,7 +122,7 @@ public class CandidatePool {
if candidateRows.isEmpty { break }
let firstRow = candidateRows[0]
let newSubIndex = min(currentSubIndex, firstRow.count - 1)
highlight(at: firstRow[newSubIndex].index)
highlightHorizontal(at: firstRow[newSubIndex].index)
break
}
if currentRowNumber >= candidateRows.count - 1 { currentRowNumber = candidateRows.count - 1 }
@ -80,13 +132,13 @@ public class CandidatePool {
}
let targetRow = candidateRows[currentRowNumber - 1]
let newSubIndex = min(result, targetRow.count - 1)
highlight(at: targetRow[newSubIndex].index)
highlightHorizontal(at: targetRow[newSubIndex].index)
case .down:
if currentRowNumber >= candidateRows.count - 1 {
if candidateRows.isEmpty { break }
let finalRow = candidateRows[candidateRows.count - 1]
let newSubIndex = min(currentSubIndex, finalRow.count - 1)
highlight(at: finalRow[newSubIndex].index)
highlightHorizontal(at: finalRow[newSubIndex].index)
break
}
if candidateRows[currentRowNumber].count != candidateRows[currentRowNumber + 1].count {
@ -95,11 +147,40 @@ public class CandidatePool {
}
let targetRow = candidateRows[currentRowNumber + 1]
let newSubIndex = min(result, targetRow.count - 1)
highlight(at: targetRow[newSubIndex].index)
highlightHorizontal(at: targetRow[newSubIndex].index)
}
}
public func highlight(at indexSpecified: Int) {
public func selectNewNeighborColumn(direction: HorizontalDirection) {
let currentSubIndex = candidateDataAll[highlightedIndex].subIndex
switch direction {
case .left:
if currentColumnNumber <= 0 {
if candidateColumns.isEmpty { break }
let firstColumn = candidateColumns[0]
let newSubIndex = min(currentSubIndex, firstColumn.count - 1)
highlightVertical(at: firstColumn[newSubIndex].index)
break
}
if currentColumnNumber >= candidateColumns.count - 1 { currentColumnNumber = candidateColumns.count - 1 }
let targetColumn = candidateColumns[currentColumnNumber - 1]
let newSubIndex = min(currentSubIndex, targetColumn.count - 1)
highlightVertical(at: targetColumn[newSubIndex].index)
case .right:
if currentColumnNumber >= candidateColumns.count - 1 {
if candidateColumns.isEmpty { break }
let finalColumn = candidateColumns[candidateColumns.count - 1]
let newSubIndex = min(currentSubIndex, finalColumn.count - 1)
highlightVertical(at: finalColumn[newSubIndex].index)
break
}
let targetColumn = candidateColumns[currentColumnNumber + 1]
let newSubIndex = min(currentSubIndex, targetColumn.count - 1)
highlightVertical(at: targetColumn[newSubIndex].index)
}
}
public func highlightHorizontal(at indexSpecified: Int) {
var indexSpecified = indexSpecified
highlightedIndex = indexSpecified
if !(0..<candidateDataAll.count).contains(highlightedIndex) {
@ -120,13 +201,48 @@ public class CandidatePool {
candidate.isSelected = (indexSpecified == i)
if candidate.isSelected { currentRowNumber = candidate.whichRow }
}
for (i, candidateColumn) in candidateRows.enumerated() {
for (i, candidateRow) in candidateRows.enumerated() {
if i != currentRowNumber {
candidateRow.forEach {
$0.key = " "
}
} else {
for (i, neta) in candidateRow.enumerated() {
neta.key = selectionKeys.map { String($0) }[i]
}
}
}
}
public func highlightVertical(at indexSpecified: Int) {
var indexSpecified = indexSpecified
highlightedIndex = indexSpecified
if !(0..<candidateDataAll.count).contains(highlightedIndex) {
NSSound.beep()
switch highlightedIndex {
case candidateDataAll.count...:
currentColumnNumber = candidateColumns.count - 1
highlightedIndex = max(0, candidateDataAll.count - 1)
indexSpecified = highlightedIndex
case ..<0:
highlightedIndex = 0
currentColumnNumber = 0
indexSpecified = highlightedIndex
default: break
}
}
for (i, candidate) in candidateDataAll.enumerated() {
candidate.isSelected = (indexSpecified == i)
if candidate.isSelected { currentColumnNumber = candidate.whichColumn }
}
for (i, candidateColumn) in candidateColumns.enumerated() {
if i != currentColumnNumber {
candidateColumn.forEach {
$0.key = " "
}
} else {
for (i, neta) in candidateColumn.enumerated() {
if neta.key.isEmpty { continue }
neta.key = selectionKeys.map { String($0) }[i]
}
}

View File

@ -11,7 +11,6 @@ import Shared
open class CtlCandidate: NSWindowController, CtlCandidateProtocol {
open var hint: String = ""
open var showPageButtons: Bool = false
open var currentLayout: NSUserInterfaceLayoutOrientation = .horizontal
open var locale: String = ""
open var useLangIdentifier: Bool = false
@ -121,6 +120,14 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol {
open var tooltip: String = ""
@discardableResult open func showNextLine() -> Bool {
false
}
@discardableResult open func showPreviousLine() -> Bool {
false
}
@discardableResult open func highlightNextCandidate() -> Bool {
false
}

View File

@ -1,122 +0,0 @@
// (c) 2022 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 CocoaExtension
import Shared
import SwiftUI
@available(macOS 12, *)
public class CtlCandidateTDK: CtlCandidate {
public var thePool: CandidatePool = .init(candidates: [])
public var theView: VwrCandidateTDK { .init(controller: self, thePool: thePool, hint: hint) }
public required init(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) {
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
let panel = NSPanel(
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 2)
panel.hasShadow = true
panel.isOpaque = false
panel.backgroundColor = NSColor.clear
contentRect.origin = NSPoint.zero
super.init(layout)
window = panel
currentLayout = layout
reloadData()
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func reloadData() {
CandidateCellData.highlightBackground = highlightedColor()
CandidateCellData.unifiedSize = candidateFont.pointSize
guard let delegate = delegate else { return }
thePool = .init(
candidates: delegate.candidatePairs(conv: true).map(\.1),
selectionKeys: delegate.selectionKeys, locale: locale
)
thePool.highlight(at: 0)
updateDisplay()
}
override open func updateDisplay() {
DispatchQueue.main.async { [self] in
let newView = NSHostingView(rootView: theView.fixedSize())
let newSize = newView.fittingSize
var newFrame = NSRect.zero
if let window = window { newFrame = window.frame }
newFrame.size = newSize
window?.setFrame(newFrame, display: false)
window?.contentView = NSHostingView(rootView: theView.fixedSize())
window?.setContentSize(newSize)
}
}
@discardableResult override public func showNextPage() -> Bool {
for _ in 0..<thePool.maximumLinesPerPage {
thePool.selectNewNeighborRow(direction: .down)
}
updateDisplay()
return true
}
@discardableResult override public func showPreviousPage() -> Bool {
for _ in 0..<thePool.maximumLinesPerPage {
thePool.selectNewNeighborRow(direction: .up)
}
updateDisplay()
return true
}
@discardableResult public func showNextLine() -> Bool {
thePool.selectNewNeighborRow(direction: .down)
updateDisplay()
return true
}
@discardableResult public func showPreviousLine() -> Bool {
thePool.selectNewNeighborRow(direction: .up)
updateDisplay()
return true
}
@discardableResult override public func highlightNextCandidate() -> Bool {
thePool.highlight(at: thePool.highlightedIndex + 1)
updateDisplay()
return true
}
@discardableResult override public func highlightPreviousCandidate() -> Bool {
thePool.highlight(at: thePool.highlightedIndex - 1)
updateDisplay()
return true
}
override public func candidateIndexAtKeyLabelIndex(_ id: Int) -> Int {
let currentRow = thePool.candidateRows[thePool.currentRowNumber]
let actualID = max(0, min(id, currentRow.count - 1))
return thePool.candidateRows[thePool.currentRowNumber][actualID].index
}
override public var selectedCandidateIndex: Int {
get {
thePool.highlightedIndex
}
set {
thePool.highlight(at: newValue)
updateDisplay()
}
}
}

View File

@ -0,0 +1,209 @@
// (c) 2022 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 CocoaExtension
import Shared
import SwiftUI
@available(macOS 12, *)
public class CtlCandidateTDK: CtlCandidate {
public var thePoolHorizontal: CandidatePool = .init(candidates: [], rowCapacity: 6)
public var theViewHorizontal: VwrCandidateHorizontal {
.init(controller: self, thePool: thePoolHorizontal, hint: hint)
}
public var thePoolVertical: CandidatePool = .init(candidates: [], columnCapacity: 6)
public var theViewVertical: VwrCandidateVertical { .init(controller: self, thePool: thePoolVertical, hint: hint) }
public required init(_ layout: NSUserInterfaceLayoutOrientation = .horizontal) {
var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0)
let styleMask: NSWindow.StyleMask = [.nonactivatingPanel]
let panel = NSPanel(
contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false
)
panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 2)
panel.hasShadow = true
panel.isOpaque = false
panel.backgroundColor = NSColor.clear
contentRect.origin = NSPoint.zero
super.init(layout)
window = panel
currentLayout = layout
reloadData()
}
@available(*, unavailable)
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func reloadData() {
CandidateCellData.highlightBackground = highlightedColor()
CandidateCellData.unifiedSize = candidateFont.pointSize
guard let delegate = delegate else { return }
switch currentLayout {
case .horizontal:
thePoolHorizontal = .init(
candidates: delegate.candidatePairs(conv: true).map(\.1), rowCapacity: 6,
selectionKeys: delegate.selectionKeys, locale: locale
)
thePoolHorizontal.highlightHorizontal(at: 0)
case .vertical:
thePoolVertical = .init(
candidates: delegate.candidatePairs(conv: true).map(\.1), columnCapacity: 6,
selectionKeys: delegate.selectionKeys, locale: locale
)
thePoolVertical.highlightVertical(at: 0)
@unknown default:
return
}
updateDisplay()
}
override open func updateDisplay() {
switch currentLayout {
case .horizontal:
DispatchQueue.main.async { [self] in
let newView = NSHostingView(rootView: theViewHorizontal)
let newSize = newView.fittingSize
window?.contentView = newView
window?.setContentSize(newSize)
}
case .vertical:
DispatchQueue.main.async { [self] in
let newView = NSHostingView(rootView: theViewVertical)
let newSize = newView.fittingSize
window?.contentView = newView
window?.setContentSize(newSize)
}
@unknown default:
return
}
}
@discardableResult override public func showNextPage() -> Bool {
switch currentLayout {
case .horizontal:
for _ in 0..<thePoolHorizontal.maximumRowsPerPage {
thePoolHorizontal.selectNewNeighborRow(direction: .down)
}
case .vertical:
for _ in 0..<thePoolVertical.maximumColumnsPerPage {
thePoolVertical.selectNewNeighborColumn(direction: .right)
}
@unknown default:
return false
}
updateDisplay()
return true
}
@discardableResult override public func showPreviousPage() -> Bool {
switch currentLayout {
case .horizontal:
for _ in 0..<thePoolHorizontal.maximumRowsPerPage {
thePoolHorizontal.selectNewNeighborRow(direction: .up)
}
case .vertical:
for _ in 0..<thePoolVertical.maximumColumnsPerPage {
thePoolVertical.selectNewNeighborColumn(direction: .left)
}
@unknown default:
return false
}
updateDisplay()
return true
}
@discardableResult override public func showNextLine() -> Bool {
switch currentLayout {
case .horizontal:
thePoolHorizontal.selectNewNeighborRow(direction: .down)
case .vertical:
thePoolVertical.selectNewNeighborColumn(direction: .right)
@unknown default:
return false
}
updateDisplay()
return true
}
@discardableResult override public func showPreviousLine() -> Bool {
switch currentLayout {
case .horizontal:
thePoolHorizontal.selectNewNeighborRow(direction: .up)
case .vertical:
thePoolVertical.selectNewNeighborColumn(direction: .left)
@unknown default:
return false
}
updateDisplay()
return true
}
@discardableResult override public func highlightNextCandidate() -> Bool {
switch currentLayout {
case .horizontal:
thePoolHorizontal.highlightHorizontal(at: thePoolHorizontal.highlightedIndex + 1)
case .vertical:
thePoolVertical.highlightVertical(at: thePoolVertical.highlightedIndex + 1)
@unknown default:
return false
}
updateDisplay()
return true
}
@discardableResult override public func highlightPreviousCandidate() -> Bool {
switch currentLayout {
case .horizontal:
thePoolHorizontal.highlightHorizontal(at: thePoolHorizontal.highlightedIndex - 1)
case .vertical:
thePoolVertical.highlightVertical(at: thePoolVertical.highlightedIndex - 1)
@unknown default:
return false
}
updateDisplay()
return true
}
override public func candidateIndexAtKeyLabelIndex(_ id: Int) -> Int {
switch currentLayout {
case .horizontal:
let currentRow = thePoolHorizontal.candidateRows[thePoolHorizontal.currentRowNumber]
let actualID = max(0, min(id, currentRow.count - 1))
return thePoolHorizontal.candidateRows[thePoolHorizontal.currentRowNumber][actualID].index
case .vertical:
let currentColumn = thePoolVertical.candidateColumns[thePoolVertical.currentColumnNumber]
let actualID = max(0, min(id, currentColumn.count - 1))
return thePoolVertical.candidateColumns[thePoolVertical.currentColumnNumber][actualID].index
@unknown default:
return 0
}
}
override public var selectedCandidateIndex: Int {
get {
switch currentLayout {
case .horizontal: return thePoolHorizontal.highlightedIndex
case .vertical: return thePoolVertical.highlightedIndex
@unknown default: return 0
}
}
set {
switch currentLayout {
case .horizontal: thePoolHorizontal.highlightHorizontal(at: newValue)
case .vertical: thePoolVertical.highlightVertical(at: newValue)
@unknown default: return
}
updateDisplay()
}
}
}

View File

@ -13,27 +13,28 @@ import SwiftUI
// MARK: - Some useless tests
@available(macOS 12, *)
struct CandidatePoolViewUI_Previews: PreviewProvider {
struct CandidatePoolViewUIHorizontal_Previews: PreviewProvider {
@State static var testCandidates: [String] = [
"八月中秋山林涼", "八月中秋", "風吹大地", "山林涼", "草枝擺", "八月", "中秋",
"🐂🍺🐂🍺", "🐃🍺", "🐂🍺", "🐃🐂🍺🍺", "🐂🍺", "🐃🍺", "🐂🍺", "🐃🍺", "🐂🍺", "🐃🍺",
"山林", "風吹", "大地", "草枝", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "",
]
static var thePool: CandidatePool {
let result = CandidatePool(candidates: testCandidates, columnCapacity: 6)
let result = CandidatePool(candidates: testCandidates, rowCapacity: 6)
// 使
result.highlight(at: 14)
result.highlightHorizontal(at: 5)
return result
}
static var previews: some View {
VwrCandidateTDK(controller: .init(.horizontal), thePool: thePool).fixedSize()
VwrCandidateHorizontal(controller: .init(.horizontal), thePool: thePool).fixedSize()
}
}
@available(macOS 12, *)
public struct VwrCandidateTDK: View {
public struct VwrCandidateHorizontal: View {
public var controller: CtlCandidateTDK
@State public var thePool: CandidatePool
@State public var hint: String = ""
@ -52,26 +53,28 @@ public struct VwrCandidateTDK: View {
VStack(alignment: .leading, spacing: 0) {
ScrollView(.vertical, showsIndicators: true) {
VStack(alignment: .leading, spacing: 1.6) {
ForEach(thePool.rangeForCurrentPage, id: \.self) { columnIndex in
ForEach(thePool.rangeForCurrentHorizontalPage, id: \.self) { rowIndex in
HStack(spacing: 10) {
ForEach(Array(thePool.candidateRows[columnIndex]), id: \.self) { currentCandidate in
ForEach(Array(thePool.candidateRows[rowIndex]), id: \.self) { currentCandidate in
currentCandidate.attributedStringForSwiftUI.fixedSize()
.frame(maxWidth: .infinity, alignment: .topLeading)
.frame(
maxWidth: .infinity,
alignment: .topLeading
)
.contentShape(Rectangle())
.onTapGesture { didSelectCandidateAt(currentCandidate.index) }
}
Spacer()
}.frame(
minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading
).id(columnIndex)
).id(rowIndex)
Divider()
}
if thePool.maximumLinesPerPage - thePool.rangeForCurrentPage.count > 0 {
ForEach(thePool.rangeForLastPageBlanked, id: \.self) { _ in
if thePool.maximumRowsPerPage - thePool.rangeForCurrentHorizontalPage.count > 0 {
ForEach(thePool.rangeForLastHorizontalPageBlanked, id: \.self) { _ in
HStack(spacing: 0) {
thePool.blankCell.attributedStringForSwiftUI.fixedSize()
thePool.blankCell.attributedStringForSwiftUI
.frame(maxWidth: .infinity, alignment: .topLeading)
.contentShape(Rectangle())
Spacer()
@ -87,41 +90,20 @@ public struct VwrCandidateTDK: View {
}
.fixedSize(horizontal: false, vertical: true).padding(5)
.background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea())
HStack(alignment: .bottom) {
Text(hint).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(1)
Spacer()
Text(positionLabel).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(
1)
}.padding(6).foregroundColor(.init(nsColor: .controlTextColor))
.shadow(color: .init(nsColor: .textBackgroundColor), radius: 1)
ZStack(alignment: .leading) {
Color(nsColor: hint.isEmpty ? .windowBackgroundColor : CandidateCellData.highlightBackground).ignoresSafeArea()
HStack(alignment: .bottom) {
Text(hint).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(1)
Spacer()
Text(positionLabel).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
.lineLimit(
1)
}
.padding(6).foregroundColor(
.init(nsColor: hint.isEmpty ? .controlTextColor : .selectedMenuItemTextColor.withAlphaComponent(0.9))
)
}
}
.frame(minWidth: thePool.maxWindowWidth, maxWidth: thePool.maxWindowWidth)
}
}
@available(macOS 12, *)
extension CandidateCellData {
public var attributedStringForSwiftUI: some View {
var result: some View {
ZStack(alignment: .leading) {
if isSelected {
Color(nsColor: CandidateCellData.highlightBackground).ignoresSafeArea().cornerRadius(6)
}
VStack(spacing: 0) {
HStack(spacing: 4) {
if UserDefaults.standard.bool(forKey: UserDef.kHandleDefaultCandidateFontsByLangIdentifier.rawValue) {
Text(AttributedString(attributedStringHeader)).frame(width: CandidateCellData.unifiedSize / 2)
Text(AttributedString(attributedString))
} else {
Text(key).font(.system(size: fontSizeKey).monospaced())
.foregroundColor(.init(nsColor: fontColorKey)).lineLimit(1)
Text(displayedText).font(.system(size: fontSizeCandidate))
.foregroundColor(.init(nsColor: fontColorCandidate)).lineLimit(1)
}
}.padding(4)
}
}.fixedSize(horizontal: false, vertical: true)
}
return result
}
}

View File

@ -0,0 +1,109 @@
// (c) 2022 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 Shared
import SwiftUI
// MARK: - Some useless tests
@available(macOS 12, *)
struct CandidatePoolViewUIVertical_Previews: PreviewProvider {
@State static var testCandidates: [String] = [
"八月中秋山林涼", "八月中秋", "風吹大地", "山林涼", "草枝擺", "🐂🍺", "🐃🍺", "八月", "中秋",
"山林", "風吹", "大地", "草枝", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "",
]
static var thePool: CandidatePool {
let result = CandidatePool(candidates: testCandidates, columnCapacity: 6, selectionKeys: "123456789")
// 使
result.highlightVertical(at: 5)
return result
}
static var previews: some View {
VwrCandidateVertical(controller: .init(.horizontal), thePool: thePool).fixedSize()
}
}
@available(macOS 12, *)
public struct VwrCandidateVertical: View {
public var controller: CtlCandidateTDK
@State public var thePool: CandidatePool
@State public var hint: String = ""
private var positionLabel: String {
(thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description
}
private func didSelectCandidateAt(_ pos: Int) {
if let delegate = controller.delegate {
delegate.candidatePairSelected(at: pos)
}
}
public var body: some View {
VStack(alignment: .leading, spacing: 0) {
ScrollView(.horizontal, showsIndicators: true) {
HStack(alignment: .top, spacing: 10) {
ForEach(thePool.rangeForCurrentVerticalPage, id: \.self) { columnIndex in
VStack(alignment: .leading, spacing: 0) {
ForEach(Array(thePool.candidateColumns[columnIndex]), id: \.self) { currentCandidate in
HStack(spacing: 0) {
currentCandidate.attributedStringForSwiftUI.fixedSize(horizontal: false, vertical: true)
.frame(
maxWidth: .infinity,
alignment: .topLeading
)
.contentShape(Rectangle())
.onTapGesture { didSelectCandidateAt(currentCandidate.index) }
}
}
}.frame(
minWidth: Double(CandidateCellData.unifiedSize * 5),
alignment: .topLeading
).id(columnIndex)
Divider()
}
if thePool.maximumColumnsPerPage - thePool.rangeForCurrentVerticalPage.count > 0 {
ForEach(thePool.rangeForLastVerticalPageBlanked, id: \.self) { _ in
VStack(alignment: .leading, spacing: 0) {
ForEach(0..<thePool.maxColumnCapacity, id: \.self) { _ in
thePool.blankCell.attributedStringForSwiftUI.fixedSize()
.frame(width: Double(CandidateCellData.unifiedSize * 5), alignment: .topLeading)
.contentShape(Rectangle())
}
}.frame(
minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading
)
Divider()
}
}
}
}
.fixedSize(horizontal: true, vertical: false).padding(5)
.background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea())
ZStack(alignment: .leading) {
Color(nsColor: hint.isEmpty ? .windowBackgroundColor : CandidateCellData.highlightBackground).ignoresSafeArea()
HStack(alignment: .bottom) {
Text(hint).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold)).lineLimit(1)
Spacer()
Text(positionLabel).font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
.lineLimit(
1)
}
.padding(6).foregroundColor(
.init(nsColor: hint.isEmpty ? .controlTextColor : .selectedMenuItemTextColor.withAlphaComponent(0.9))
)
}
}
}
}

View File

@ -19,7 +19,7 @@ final class CandidatePoolTests: XCTestCase {
]
func testPoolHorizontal() throws {
let pool = CandidatePool(candidates: testCandidates, columnCapacity: 8)
let pool = CandidatePool(candidates: testCandidates, rowCapacity: 6)
var strOutput = ""
pool.candidateRows.forEach {
$0.forEach {
@ -32,9 +32,9 @@ final class CandidatePoolTests: XCTestCase {
}
func testPoolVertical() throws {
let pool = CandidatePool(candidates: testCandidates, columnCapacity: 8)
let pool = CandidatePool(candidates: testCandidates, columnCapacity: 6)
var strOutput = ""
pool.candidateRows.forEach {
pool.candidateColumns.forEach {
$0.forEach {
strOutput += $0.displayedText + ", "
}

View File

@ -13,13 +13,15 @@ let package = Package(
)
],
dependencies: [
.package(path: "../vChewing_CocoaExtension")
.package(path: "../vChewing_CocoaExtension"),
.package(path: "../vChewing_SwiftExtension")
],
targets: [
.target(
name: "Shared",
dependencies: [
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension")
.product(name: "CocoaExtension", package: "vChewing_CocoaExtension"),
.product(name: "SwiftExtension", package: "vChewing_SwiftExtension")
]
)
]

View File

@ -25,7 +25,8 @@ public class CandidateCellData: Hashable {
public var displayedText: String
public var size: Double { Self.unifiedSize }
public var isSelected: Bool = false
public var whichRow: Int = 0
public var whichRow: Int = 0 //
public var whichColumn: Int = 0 //
public var index: Int = 0
public var subIndex: Int = 0
@ -48,7 +49,7 @@ public class CandidateCellData: Hashable {
let rect = attributedStringForLengthCalculation.boundingRect(
with: NSSize(width: 1600.0, height: 1600.0), options: [.usesLineFragmentOrigin]
)
let rawResult = ceil(rect.width + size / size)
let rawResult = ceil(rect.width)
return Int(rawResult)
}
@ -73,8 +74,16 @@ public class CandidateCellData: Hashable {
}
public var attributedStringForLengthCalculation: NSAttributedString {
let paraStyleKey = NSMutableParagraphStyle()
paraStyleKey.setParagraphStyle(NSParagraphStyle.default)
paraStyleKey.alignment = .natural
let paraStyle = NSMutableParagraphStyle()
paraStyle.setParagraphStyle(NSParagraphStyle.default)
paraStyle.alignment = .natural
paraStyle.lineBreakMode = .byWordWrapping
let attrCandidate: [NSAttributedString.Key: AnyObject] = [
.font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular)
.font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular),
.paragraphStyle: paraStyle,
]
let attrStrCandidate = NSMutableAttributedString(string: displayedText + " ", attributes: attrCandidate)
return attrStrCandidate
@ -87,6 +96,7 @@ public class CandidateCellData: Hashable {
let paraStyle = NSMutableParagraphStyle()
paraStyle.setParagraphStyle(NSParagraphStyle.default)
paraStyle.alignment = .natural
paraStyle.lineBreakMode = .byWordWrapping
var attrCandidate: [NSAttributedString.Key: AnyObject] = [
.font: NSFont.monospacedDigitSystemFont(ofSize: size, weight: .regular),
.paragraphStyle: paraStyle,

View File

@ -30,13 +30,14 @@ public protocol CtlCandidateProtocol {
var candidateFont: NSFont { get set }
var tooltip: String { get set }
var useLangIdentifier: Bool { get set }
var showPageButtons: Bool { get set }
init(_ layout: NSUserInterfaceLayoutOrientation)
func reloadData()
func updateDisplay()
func showNextPage() -> Bool
func showPreviousPage() -> Bool
func showNextLine() -> Bool
func showPreviousLine() -> Bool
func highlightNextCandidate() -> Bool
func highlightPreviousCandidate() -> Bool
func candidateIndexAtKeyLabelIndex(_: Int) -> Int

View File

@ -19,7 +19,7 @@ public protocol PrefMgrProtocol {
var keyboardParser: Int { get set }
var basicKeyboardLayout: String { get set }
var alphanumericalKeyboardLayout: String { get set }
var showPageButtonsInCandidateWindow: Bool { get set }
var showNotificationsWhenTogglingCapsLock: Bool { get set }
var candidateListTextSize: Double { get set }
var shouldAutoReloadUserDataFiles: Bool { get set }
var useRearCursorMode: Bool { get set }

View File

@ -7,6 +7,7 @@
// requirements defined in MIT License.
import Foundation
import SwiftExtension
// MARK: - UserDef Snapshot Manager
@ -20,7 +21,7 @@ public enum UserDef: String, CaseIterable {
case kKeyboardParser = "KeyboardParser"
case kBasicKeyboardLayout = "BasicKeyboardLayout"
case kAlphanumericalKeyboardLayout = "AlphanumericalKeyboardLayout"
case kShowPageButtonsInCandidateWindow = "ShowPageButtonsInCandidateWindow"
case kShowNotificationsWhenTogglingCapsLock = "ShowNotificationsWhenTogglingCapsLock"
case kCandidateListTextSize = "CandidateListTextSize"
case kAppleLanguages = "AppleLanguages"
case kShouldAutoReloadUserDataFiles = "ShouldAutoReloadUserDataFiles"
@ -214,10 +215,10 @@ public enum CandidateKey {
return NSLocalizedString("There should not be duplicated keys.", comment: "")
case .tooShort:
return NSLocalizedString(
"Please specify at least 4 candidate keys.", comment: ""
"Please specify at least 6 candidate keys.", comment: ""
)
case .tooLong:
return NSLocalizedString("Maximum 15 candidate keys allowed.", comment: "")
return NSLocalizedString("Maximum 9 candidate keys allowed.", comment: "")
}
}
}
@ -233,10 +234,10 @@ public enum CandidateKey {
if trimmed.contains(" ") {
throw CandidateKey.ErrorType.containSpace
}
if trimmed.count < 4 {
if trimmed.count < 6 {
throw CandidateKey.ErrorType.tooShort
}
if trimmed.count > 15 {
if trimmed.count > 9 {
throw CandidateKey.ErrorType.tooLong
}
let set = Set(Array(trimmed))

View File

@ -13,7 +13,7 @@ import Foundation
// Extend the RangeReplaceableCollection to allow it clean duplicated characters.
// Ref: https://stackoverflow.com/questions/25738817/
extension RangeReplaceableCollection where Element: Hashable {
public var deduplicate: Self {
public var deduplicated: Self {
var set = Set<Element>()
return filter { set.insert($0).inserted }
}

View File

@ -275,7 +275,7 @@ public class KeyHandler {
Megrez.Compositor.KeyValuePaired(key: $0.0, value: $0.1.value)
}
arrCandidates = arrSuggestedCandidates.filter { arrCandidates.contains($0) } + arrCandidates
arrCandidates = arrCandidates.deduplicate
arrCandidates = arrCandidates.deduplicated
arrCandidates = arrCandidates.stableSort { $0.key.split(separator: "-").count > $1.key.split(separator: "-").count }
return arrCandidates.map { ($0.key, $0.value) }
}

View File

@ -74,8 +74,8 @@ extension KeyHandler {
let updated: Bool =
prefs.specifyShiftTabKeyBehavior
? (input.isShiftHold
? ctlCandidate.showPreviousPage()
: ctlCandidate.showNextPage())
? ctlCandidate.showPreviousLine()
: ctlCandidate.showNextLine())
: (input.isShiftHold
? ctlCandidate.highlightPreviousCandidate()
: ctlCandidate.highlightNextCandidate())
@ -92,9 +92,9 @@ extension KeyHandler {
prefs.specifyShiftSpaceKeyBehavior
? (input.isShiftHold
? ctlCandidate.highlightNextCandidate()
: ctlCandidate.showNextPage())
: ctlCandidate.showNextLine())
: (input.isShiftHold
? ctlCandidate.showNextPage()
? ctlCandidate.showNextLine()
: ctlCandidate.highlightNextCandidate())
if !updated {
errorCallback("A11C781F")
@ -131,7 +131,7 @@ extension KeyHandler {
errorCallback("1145148D")
}
case .vertical:
if !ctlCandidate.showPreviousPage() {
if !ctlCandidate.showPreviousLine() {
errorCallback("1919810D")
}
@unknown default:
@ -149,7 +149,7 @@ extension KeyHandler {
errorCallback("9B65138D")
}
case .vertical:
if !ctlCandidate.showNextPage() {
if !ctlCandidate.showNextLine() {
errorCallback("9244908D")
}
@unknown default:
@ -163,21 +163,8 @@ extension KeyHandler {
if input.isUp {
switch ctlCandidate.currentLayout {
case .horizontal:
if #available(macOS 12, *) {
if let ctlCandidate = ctlCandidate as? CtlCandidateTDK {
ctlCandidate.showPreviousLine()
break
} else {
if !ctlCandidate.showPreviousPage() {
errorCallback("9B614524")
break
}
}
} else {
if !ctlCandidate.showPreviousPage() {
errorCallback("9B614524")
break
}
if !ctlCandidate.showPreviousLine() {
errorCallback("9B614524")
}
case .vertical:
if !ctlCandidate.highlightPreviousCandidate() {
@ -194,21 +181,9 @@ extension KeyHandler {
if input.isDown {
switch ctlCandidate.currentLayout {
case .horizontal:
if #available(macOS 12, *) {
if let ctlCandidate = ctlCandidate as? CtlCandidateTDK {
ctlCandidate.showNextLine()
break
} else {
if !ctlCandidate.showNextPage() {
errorCallback("92B990DD")
break
}
}
} else {
if !ctlCandidate.showNextPage() {
errorCallback("92B990DD")
break
}
if !ctlCandidate.showNextLine() {
errorCallback("92B990DD")
break
}
case .vertical:
if !ctlCandidate.highlightNextCandidate() {
@ -322,15 +297,7 @@ extension KeyHandler {
if input.isSymbolMenuPhysicalKey {
var updated = true
if #available(macOS 12, *) {
if let ctlCandidate = ctlCandidate as? CtlCandidateTDK {
updated = input.isShiftHold ? ctlCandidate.showPreviousLine() : ctlCandidate.showNextLine()
} else {
updated = input.isShiftHold ? ctlCandidate.showPreviousPage() : ctlCandidate.showNextPage()
}
} else {
updated = input.isShiftHold ? ctlCandidate.showPreviousPage() : ctlCandidate.showNextPage()
}
updated = input.isShiftHold ? ctlCandidate.showPreviousLine() : ctlCandidate.showNextLine()
if !updated {
errorCallback("66F3477B")
}

View File

@ -53,8 +53,8 @@ public class PrefMgr: PrefMgrProtocol {
)
public var alphanumericalKeyboardLayout: String
@AppProperty(key: UserDef.kShowPageButtonsInCandidateWindow.rawValue, defaultValue: true)
public var showPageButtonsInCandidateWindow: Bool
@AppProperty(key: UserDef.kShowNotificationsWhenTogglingCapsLock.rawValue, defaultValue: true)
public var showNotificationsWhenTogglingCapsLock: Bool
@AppProperty(key: UserDef.kCandidateListTextSize.rawValue, defaultValue: 18)
public var candidateListTextSize: Double {
@ -233,7 +233,10 @@ public class PrefMgr: PrefMgrProtocol {
@AppProperty(key: UserDef.kCandidateKeys.rawValue, defaultValue: kDefaultCandidateKeys)
public var candidateKeys: String {
didSet {
if useIMKCandidateWindow {
if candidateKeys != candidateKeys.deduplicated {
candidateKeys = candidateKeys.deduplicated
}
if !(6...9).contains(candidateKeys.count) {
candidateKeys = Self.kDefaultCandidateKeys
}
}

View File

@ -22,6 +22,8 @@ extension PrefMgr {
disableShiftTogglingAlphanumericalMode = false
togglingAlphanumericalModeWithLShift = false
}
// ( didSet )
candidateKeys = candidateKeys
//
clientsIMKTextInputIncapable = Array(Set(clientsIMKTextInputIncapable)).sorted()
//

View File

@ -47,7 +47,7 @@ class SessionCtl: IMKInputController {
// MARK: -
/// CapsLock
/// Caps Lock
var isCapsLocked: Bool { NSEvent.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock) }
/// SessionCtl
@ -68,7 +68,7 @@ class SessionCtl: IMKInputController {
}
/// Shift
/// - Remark: Struct CapsLock Shift
/// - Remark: Struct Caps Lock Shift
static var theShiftKeyDetector = ShiftKeyUpChecker(useLShift: PrefMgr.shared.togglingAlphanumericalModeWithLShift)
/// `handle(event:)` Shift
@ -170,7 +170,7 @@ extension SessionCtl {
keyHandler.ensureKeyboardParser()
if isASCIIMode, !isCapsLocked, PrefMgr.shared.disableShiftTogglingAlphanumericalMode { isASCIIMode = false }
if isCapsLocked { isASCIIMode = isCapsLocked } // CapsLock
if isCapsLocked { isASCIIMode = isCapsLocked } // Caps Lock
///
/// macOS

View File

@ -142,7 +142,6 @@ extension SessionCtl {
Self.ctlCandidateCurrent.hint = NSLocalizedString("Hold ⇧ to choose associates.", comment: "")
}
Self.ctlCandidateCurrent.showPageButtons = PrefMgr.shared.showPageButtonsInCandidateWindow
Self.ctlCandidateCurrent.useLangIdentifier = PrefMgr.shared.handleDefaultCandidateFontsByLangIdentifier
Self.ctlCandidateCurrent.locale = {
switch inputMode {

View File

@ -37,14 +37,18 @@ extension SessionCtl {
// Caps Lock
if event.type == .flagsChanged, event.keyCode == KeyCode.kCapsLock.rawValue {
let isCapsLockTurnedOn = event.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock)
let status = NSLocalizedString("NotificationSwitchASCII", comment: "")
Notifier.notify(
message: isCapsLockTurnedOn
? "Caps Lock" + NSLocalizedString("Alphanumerical Input Mode", comment: "") + "\n" + status
: NSLocalizedString("Chinese Input Mode", comment: "") + "\n" + status
)
isASCIIMode = isCapsLockTurnedOn
DispatchQueue.main.async {
let isCapsLockTurnedOn = event.modifierFlags.intersection(.deviceIndependentFlagsMask).contains(.capsLock)
let status = NSLocalizedString("NotificationSwitchASCII", comment: "")
if PrefMgr.shared.showNotificationsWhenTogglingCapsLock {
Notifier.notify(
message: isCapsLockTurnedOn
? "Caps Lock" + NSLocalizedString("Alphanumerical Input Mode", comment: "") + "\n" + status
: NSLocalizedString("Chinese Input Mode", comment: "") + "\n" + status
)
}
self.isASCIIMode = isCapsLockTurnedOn
}
}
// Shift macOS 10.15 macOS

View File

@ -12,7 +12,6 @@ import Shared
/// IMKCandidates bridging header Swift Package
public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
public var hint: String = ""
public var showPageButtons: Bool = false
public var locale: String = ""
public var useLangIdentifier: Bool = false
public var currentLayout: NSUserInterfaceLayoutOrientation = .horizontal
@ -149,6 +148,18 @@ public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
return true
}
// IMK
public func showNextLine() -> Bool {
do { currentLayout == .vertical ? moveRight(self) : moveDown(self) }
return true
}
// IMK
public func showPreviousLine() -> Bool {
do { currentLayout == .vertical ? moveLeft(self) : moveUp(self) }
return true
}
public func candidateIndexAtKeyLabelIndex(_ index: Int) -> Int {
guard let delegate = delegate else { return Int.max }
let result = currentPageIndex * keyLabels.count + index

View File

@ -47,6 +47,8 @@ struct suiPrefPaneExperience: View {
forKey: UserDef.kTrimUnfinishedReadingsOnCommit.rawValue)
@State private var selAlwaysShowTooltipTextsHorizontally = UserDefaults.standard.bool(
forKey: UserDef.kAlwaysShowTooltipTextsHorizontally.rawValue)
@State private var selShowNotificationsWhenTogglingCapsLock = UserDefaults.standard.bool(
forKey: UserDef.kShowNotificationsWhenTogglingCapsLock.rawValue)
private let contentMaxHeight: Double = 440
private let contentWidth: Double = {
@ -195,6 +197,14 @@ struct suiPrefPaneExperience: View {
}
).disabled(PrefMgr.shared.disableShiftTogglingAlphanumericalMode == true)
}
Preferences.Section(title: "Caps Lock:") {
Toggle(
LocalizedStringKey("Show notifications when toggling Caps Lock"),
isOn: $selShowNotificationsWhenTogglingCapsLock.onChange {
PrefMgr.shared.showNotificationsWhenTogglingCapsLock = selShowNotificationsWhenTogglingCapsLock
}
)
}
Preferences.Section(label: { Text(LocalizedStringKey("Misc Settings:")) }) {
Toggle(
LocalizedStringKey("Enable Space key for calling candidate window"),

View File

@ -23,8 +23,6 @@ struct suiPrefPaneGeneral: View {
: ["auto"]
@State private var selEnableHorizontalCandidateLayout = UserDefaults.standard.bool(
forKey: UserDef.kUseHorizontalCandidateList.rawValue)
@State private var selShowPageButtonsInCandidateUI = UserDefaults.standard.bool(
forKey: UserDef.kShowPageButtonsInCandidateWindow.rawValue)
@State private var selEnableKanjiConvToKangXi = UserDefaults.standard.bool(
forKey: UserDef.kChineseConversionEnabled.rawValue)
@State private var selEnableKanjiConvToJIS = UserDefaults.standard.bool(
@ -129,18 +127,8 @@ struct suiPrefPaneGeneral: View {
.labelsHidden()
.horizontalRadioGroupLayout()
.pickerStyle(RadioGroupPickerStyle())
.disabled(!PrefMgr.shared.useIMKCandidateWindow)
if PrefMgr.shared.useIMKCandidateWindow {
Text(LocalizedStringKey("Choose your preferred layout of the candidate window."))
.preferenceDescription()
} else {
Text(
LocalizedStringKey(
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window."
)
)
Text(LocalizedStringKey("Choose your preferred layout of the candidate window."))
.preferenceDescription()
}
}
Preferences.Section(label: { Text(LocalizedStringKey("Output Settings:")) }) {
Toggle(

View File

@ -56,7 +56,7 @@ struct suiPrefPaneKeyboard: View {
items: CandidateKey.suggestions,
text: $selSelectionKeys.onChange {
let value = selSelectionKeys
let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicate
let keys: String = value.trimmingCharacters(in: .whitespacesAndNewlines).deduplicated
do {
try CandidateKey.validate(keys: keys)
PrefMgr.shared.candidateKeys = keys
@ -64,8 +64,8 @@ struct suiPrefPaneKeyboard: View {
} catch CandidateKey.ErrorType.empty {
selSelectionKeys = PrefMgr.shared.candidateKeys
} catch {
if let window = ctlPrefUI.shared.controller.window {
let alert = NSAlert(error: error)
if let window = ctlPrefUI.shared.controller.window, let error = error as? CandidateKey.ErrorType {
let alert = NSAlert(error: error.errorDescription)
alert.beginSheetModal(for: window) { _ in
selSelectionKeys = PrefMgr.shared.candidateKeys
}

View File

@ -135,7 +135,9 @@ class ctlPrefWindow: NSWindowController {
}
selectionKeyComboBox.stringValue = candidateSelectionKeys
selectionKeyComboBox.isEnabled = false // IMKCandidates
if PrefMgr.shared.useIMKCandidateWindow {
selectionKeyComboBox.isEnabled = false // IMKCandidates
}
}
// CNS
@ -198,7 +200,7 @@ class ctlPrefWindow: NSWindowController {
let keys = (sender as AnyObject).stringValue?.trimmingCharacters(
in: .whitespacesAndNewlines
)
.deduplicate
.deduplicated
else {
return
}
@ -209,8 +211,8 @@ class ctlPrefWindow: NSWindowController {
} catch CandidateKey.ErrorType.empty {
selectionKeyComboBox.stringValue = PrefMgr.shared.candidateKeys
} catch {
if let window = window {
let alert = NSAlert(error: error)
if let window = window, let error = error as? CandidateKey.ErrorType {
let alert = NSAlert(error: error.errorDescription)
alert.beginSheetModal(for: window) { _ in
self.selectionKeyComboBox.stringValue = PrefMgr.shared.candidateKeys
}

View File

@ -51,12 +51,11 @@
"\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude.";
"Edit Phrase Replacement Table…" = "Edit Phrase Replacement Table…";
"Use Phrase Replacement" = "Use Phrase Replacement";
"Candidates keys cannot be empty." = "Candidates keys cannot be empty.";
"Candidate keys can only contain ASCII characters like alphanumericals." = "Candidate keys can only contain ASCII characters like alphanumericals.";
"Candidate keys cannot contain space." = "Candidate keys cannot contain space.";
"There should not be duplicated keys." = "There should not be duplicated keys.";
"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys.";
"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed.";
"Please specify at least 6 candidate keys." = "Please specify at least 6 candidate keys.";
"Maximum 9 candidate keys allowed." = "Maximum 9 candidate keys allowed.";
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry.";
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.";
"Per-Char Select Mode" = "Per-Char Select Mode";
@ -209,7 +208,7 @@
"Shift+BackSpace:" = "Shift+BackSpace:";
"Shift+Letter:" = "Shift+Letter:";
"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer";
"Show page buttons in candidate window" = "Show page buttons in candidate window";
"Show notifications when toggling Caps Lock" = "Show notifications when toggling Caps Lock";
"Simplified Chinese" = "Simplified Chinese";
"Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work." = "Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work.";
"Space to +cycle candidates, Shift+Space to +cycle pages" = "Space to +cycle candidates, Shift+Space to +cycle pages";
@ -217,7 +216,6 @@
"Specify the behavior of intonation key when syllable composer is empty." = "Specify the behavior of intonation key when syllable composer is empty.";
"Starlight" = "Starlight";
"Stop farting (when typed phonetic combination is invalid, etc.)" = "Stop farting (when typed phonetic combination is invalid, etc.)";
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window.";
"This only works with Tadokoro candidate window." = "This only works with Tadokoro candidate window.";
"Traditional Chinese" = "Traditional Chinese";
"Trim unfinished readings on commit" = "Trim unfinished readings on commit";

View File

@ -51,12 +51,11 @@
"\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude.";
"Edit Phrase Replacement Table…" = "Edit Phrase Replacement Table…";
"Use Phrase Replacement" = "Use Phrase Replacement";
"Candidates keys cannot be empty." = "Candidates keys cannot be empty.";
"Candidate keys can only contain ASCII characters like alphanumericals." = "Candidate keys can only contain ASCII characters like alphanumericals.";
"Candidate keys cannot contain space." = "Candidate keys cannot contain space.";
"There should not be duplicated keys." = "There should not be duplicated keys.";
"Please specify at least 4 candidate keys." = "Please specify at least 4 candidate keys.";
"Maximum 15 candidate keys allowed." = "Maximum 15 candidate keys allowed.";
"Please specify at least 6 candidate keys." = "Please specify at least 6 candidate keys.";
"Maximum 9 candidate keys allowed." = "Maximum 9 candidate keys allowed.";
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ Phrase replacement mode enabled, interfering user phrase entry.";
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ Unhandlable: Chars and Readings in buffer doesn't match.";
"Per-Char Select Mode" = "Per-Char Select Mode";
@ -209,7 +208,7 @@
"Shift+BackSpace:" = "Shift+BackSpace:";
"Shift+Letter:" = "Shift+Letter:";
"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer";
"Show page buttons in candidate window" = "Show page buttons in candidate window";
"Show notifications when toggling Caps Lock" = "Show notifications when toggling Caps Lock";
"Simplified Chinese" = "Simplified Chinese";
"Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work." = "Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work.";
"Space to +cycle candidates, Shift+Space to +cycle pages" = "Space to +cycle candidates, Shift+Space to +cycle pages";
@ -217,7 +216,6 @@
"Specify the behavior of intonation key when syllable composer is empty." = "Specify the behavior of intonation key when syllable composer is empty.";
"Starlight" = "Starlight";
"Stop farting (when typed phonetic combination is invalid, etc.)" = "Stop farting (when typed phonetic combination is invalid, etc.)";
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window.";
"This only works with Tadokoro candidate window." = "This only works with Tadokoro candidate window.";
"Traditional Chinese" = "Traditional Chinese";
"Trim unfinished readings on commit" = "Trim unfinished readings on commit";

View File

@ -51,12 +51,11 @@
"\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」は既存語彙:\n ENTER で最優先にし、SHIFT+COMMAND+ENTER で優先順位を下げる;\n BackSpace 或いは Delete で排除。";
"Edit Phrase Replacement Table…" = "言葉置換表を編集…";
"Use Phrase Replacement" = "言葉置換機能";
"Candidates keys cannot be empty." = "言選り用キー陣列に何かキーをご登録ください。";
"Candidate keys can only contain ASCII characters like alphanumericals." = "言選り用キー陣列にはASCII文字だけをご登録ください英数など。";
"Candidate keys cannot contain space." = "言選り用キー陣列にスペースキーは登録できません。";
"There should not be duplicated keys." = "言選り用キー陣列に同じキーの重複登録はできません。";
"Please specify at least 4 candidate keys." = "言選り用キー陣列に少なくとも4つのキーをご登録ください。";
"Maximum 15 candidate keys allowed." = "言選り用キー陣列には最多15つキー登録できます。";
"Please specify at least 6 candidate keys." = "言選り用キー陣列に少なくとも6つのキーをご登録ください。";
"Maximum 9 candidate keys allowed." = "言選り用キー陣列には最多9つキー登録できます。";
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 言葉置換機能稼働中、新添付言葉にも影響。";
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 対処不可:緩衝列の字数は読みの数と不同等。";
"Per-Char Select Mode" = "全候補入力モード";
@ -82,7 +81,7 @@
"Optimize Memorized Phrases" = "臨時記憶資料を整う";
"Clear Memorized Phrases" = "臨時記憶資料を削除";
"Currency Numeral Output" = "数字大字変換";
"Hold ⇧ to choose associates." = "⇧を押しながら連想候補を選択ください。";
"Hold ⇧ to choose associates." = "⇧を押しながら連想候補を選択。";
// The followings are the category names used in the Symbol menu.
"catCommonSymbols" = "常用";
@ -209,7 +208,7 @@
"Shift+BackSpace:" = "Shift+BackSpace:";
"Shift+Letter:" = "Shift+文字キー:";
"Show Hanyu-Pinyin in the inline composition buffer" = "弁音合併入力(入力緩衝列で音読みを漢語弁音に)";
"Show page buttons in candidate window" = "入力候補陳列の側にページボタンを表示";
"Show notifications when toggling Caps Lock" = "Caps Lock で切り替えの時に吹出通知メッセージを";
"Simplified Chinese" = "簡体中国語";
"Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work." = "いくつかのアプリ(例えば MS Edge や Google Chrome などのような Chromium 系ブラウザーには「Shift キーの入力イベントを複数化してしまう」という支障があり、そしてアプリそれぞれの開発元に修復される可能性はほぼゼロだと言える。威注音入力アプリは対策用の特殊措置を普段に起用している。ご自分の必要によって設定してください。";
"Space to +cycle candidates, Shift+Space to +cycle pages" = "Shift+Space で次のページ、Space で次の候補文字を";
@ -217,7 +216,6 @@
"Specify the behavior of intonation key when syllable composer is empty." = "音読組立緩衝列が空かされた時の音調キーの行為をご指定ください。";
"Starlight" = "星光配列";
"Stop farting (when typed phonetic combination is invalid, etc.)" = "マナーモード // 外すと入力間違った時に変な声が出る";
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所候補陳列ウィンドウは格子状横型陳列しかできません。開発道場のページで IMK 候補陳列ウィンドウを起用してから、今のこのページで縦型・横型陳列の選択はできます。";
"This only works with Tadokoro candidate window." = "これは田所候補陳列ウィンドウだけに効ける機能である。";
"Traditional Chinese" = "繁体中国語";
"Trim unfinished readings on commit" = "送り出す緩衝列内容から未完成な音読みを除く";

View File

@ -51,12 +51,11 @@
"\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」已存在:\n 敲 Enter 以升权、敲 Shift+Command+Enter 以降权;\n 敲 BackSpace 或 Delete 以排除。";
"Edit Phrase Replacement Table…" = "编辑语汇置换表…";
"Use Phrase Replacement" = "使用语汇置换";
"Candidates keys cannot be empty." = "您必须指定选字键。";
"Candidate keys can only contain ASCII characters like alphanumericals." = "选字键只能是英数等 ASCII 字符。";
"Candidate keys cannot contain space." = "选字键不得包含空格。";
"There should not be duplicated keys." = "选字键不得重复。";
"Please specify at least 4 candidate keys." = "请至少指定四个选字键。";
"Maximum 15 candidate keys allowed." = "选字键最多只能指定十五个。";
"Please specify at least 6 candidate keys." = "请至少指定 6 个选字键。";
"Maximum 9 candidate keys allowed." = "选字键最多只能指定 9 个。";
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 语汇置换功能已启用,会波及语汇自订。";
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 无法处理:组字区字数与读音数不对应。";
"Per-Char Select Mode" = "模拟逐字选字输入";
@ -209,7 +208,7 @@
"Shift+BackSpace:" = "Shift+退格键:";
"Shift+Letter:" = "Shift+字母键:";
"Show Hanyu-Pinyin in the inline composition buffer" = "拼音并击(组字区内显示汉语拼音)";
"Show page buttons in candidate window" = "在选字窗内显示翻页按钮";
"Show notifications when toggling Caps Lock" = "以 Caps Lock 切换输入法/中英模式时显示通知";
"Simplified Chinese" = "简体中文";
"Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work." = "某些应用(比如像是 MS Edge 或者 Chrome 这样的 Chromium 核心的浏览器)可能会使输入的 Shift 按键讯号被重复输入,且其有关研发方很可能不打算修复这些产品缺陷。威注音针对这些应用预设启用了相容措施。请在此根据您的需求自订。";
"Space to +cycle candidates, Shift+Space to +cycle pages" = "Shift+空格键 换下一页,空格键 换选下一个候选字";
@ -217,7 +216,6 @@
"Specify the behavior of intonation key when syllable composer is empty." = "指定声调键(在注拼槽为「空」状态时)的行为。";
"Starlight" = "星光排列";
"Stop farting (when typed phonetic combination is invalid, etc.)" = "廉耻模式 // 取消勾选的话,敲错字时会有异音";
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所选字窗仅支援横排矩阵布局模式。如欲使用纵排布局模式者,请在开发道场内先启用 IMK 选字窗。";
"This only works with Tadokoro candidate window." = "该方法仅对田所选字窗起作用。";
"Traditional Chinese" = "繁体中文";
"Trim unfinished readings on commit" = "在递交时清理未完成拼写的读音";

View File

@ -51,12 +51,11 @@
"\"%@\" already exists:\n ENTER to boost, SHIFT+COMMAND+ENTER to nerf, \n BackSpace or Delete key to exclude." = "「%@」已存在:\n 敲 Enter 以升權、敲 Shift+Command+Enter 以降權;\n 敲 BackSpace 或 Delete 以排除。";
"Edit Phrase Replacement Table…" = "編輯語彙置換表…";
"Use Phrase Replacement" = "使用語彙置換";
"Candidates keys cannot be empty." = "您必須指定選字鍵。";
"Candidate keys can only contain ASCII characters like alphanumericals." = "選字鍵只能是英數等 ASCII 字符。";
"Candidate keys cannot contain space." = "選字鍵不得包含空格。";
"There should not be duplicated keys." = "選字鍵不得重複。";
"Please specify at least 4 candidate keys." = "請至少指定四個選字鍵。";
"Maximum 15 candidate keys allowed." = "選字鍵最多只能指定十五個。";
"Please specify at least 6 candidate keys." = "請至少指定 6 個選字鍵。";
"Maximum 9 candidate keys allowed." = "選字鍵最多只能指定 9 個。";
"⚠︎ Phrase replacement mode enabled, interfering user phrase entry." = "⚠︎ 語彙置換功能已啟用,會波及語彙自訂。";
"⚠︎ Unhandlable: Chars and Readings in buffer doesn't match." = "⚠︎ 無法處理:組字區字數與讀音數不對應。";
"Per-Char Select Mode" = "模擬逐字選字輸入";
@ -209,7 +208,7 @@
"Shift+BackSpace:" = "Shift+退格鍵:";
"Shift+Letter:" = "Shift+字母鍵:";
"Show Hanyu-Pinyin in the inline composition buffer" = "拼音並擊(組字區內顯示漢語拼音)";
"Show page buttons in candidate window" = "在選字窗內顯示翻頁按鈕";
"Show notifications when toggling Caps Lock" = "以 Caps Lock 切換輸入法/中英模式時顯示通知";
"Simplified Chinese" = "簡體中文";
"Some client apps (like Chromium-cored browsers: MS Edge, Google Chrome, etc.) may duplicate Shift-key inputs due to their internal bugs, and their devs are less likely to fix their bugs of such. vChewing has its accommodation procedures enabled by default for known Chromium-cored browsers. Here you can customize how the accommodation should work." = "某些應用(比如像是 MS Edge 或者 Chrome 這樣的 Chromium 核心的瀏覽器)可能會使輸入的 Shift 按鍵訊號被重複輸入,且其有關研發方很可能不打算修復這些產品缺陷。威注音針對這些應用預設啟用了相容措施。請在此根據您的需求自訂。";
"Space to +cycle candidates, Shift+Space to +cycle pages" = "Shift+空格鍵 換下一頁,空格鍵 換選下一個候選字";
@ -217,7 +216,6 @@
"Specify the behavior of intonation key when syllable composer is empty." = "指定聲調鍵(在注拼槽為「空」狀態時)的行為。";
"Starlight" = "星光排列";
"Stop farting (when typed phonetic combination is invalid, etc.)" = "廉恥模式 // 取消勾選的話,敲錯字時會有異音";
"Tadokoro candidate window only supports horizontal grid view. Enable IMK candidate window in DevZone page first if you want to choose vertical candidate window." = "田所選字窗僅支援橫排矩陣佈局模式。如欲使用縱排佈局模式者,請在開發道場內先啟用 IMK 選字窗。";
"This only works with Tadokoro candidate window." = "該方法僅對田所選字窗起作用。";
"Traditional Chinese" = "繁體中文";
"Trim unfinished readings on commit" = "在遞交時清理未完成拼寫的讀音";

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@ -36,14 +37,14 @@
</window>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<visualEffectView blendingMode="behindWindow" material="sidebar" state="followsWindowActiveState" id="XWo-36-xGi" userLabel="vwrExperience">
<rect key="frame" x="0.0" y="0.0" width="836" height="396"/>
<rect key="frame" x="0.0" y="0.0" width="836" height="418"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qDj-B9-mZf">
<rect key="frame" x="20" y="32" width="796" height="344"/>
<rect key="frame" x="20" y="31" width="796" height="367"/>
<subviews>
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="j9R-fB-ttM">
<rect key="frame" x="0.0" y="26" width="402" height="318"/>
<rect key="frame" x="0.0" y="49" width="402" height="318"/>
<subviews>
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fO5-4y-X0y">
<rect key="frame" x="0.0" y="277" width="365" height="41"/>
@ -266,10 +267,10 @@
</customSpacing>
</stackView>
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="12" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VOm-nN-5IB">
<rect key="frame" x="410" y="0.0" width="386" height="344"/>
<rect key="frame" x="410" y="0.0" width="386" height="367"/>
<subviews>
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="7" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="s47-wG-vKA">
<rect key="frame" x="0.0" y="261" width="386" height="83"/>
<rect key="frame" x="0.0" y="284" width="386" height="83"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iCL-n8-VTP">
<rect key="frame" x="-2" y="68" width="323" height="15"/>
@ -319,7 +320,7 @@
</customSpacing>
</stackView>
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NOW-jd-XBh">
<rect key="frame" x="0.0" y="188" width="386" height="61"/>
<rect key="frame" x="0.0" y="211" width="386" height="61"/>
<subviews>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="J0f-Aw-dxC">
<rect key="frame" x="-2" y="46" width="336" height="15"/>
@ -365,10 +366,10 @@
</customSpacing>
</stackView>
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jxD-fv-UYx">
<rect key="frame" x="0.0" y="0.0" width="380" height="176"/>
<rect key="frame" x="0.0" y="0.0" width="380" height="199"/>
<subviews>
<button translatesAutoresizingMaskIntoConstraints="NO" id="109">
<rect key="frame" x="-1" y="160.5" width="285" height="16"/>
<rect key="frame" x="-1" y="183.5" width="285" height="16"/>
<buttonCell key="cell" type="check" title="Enable Space key for calling candidate window" bezelStyle="regularSquare" imagePosition="left" alignment="left" controlSize="small" state="on" inset="2" id="110">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -378,7 +379,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bE0-Lq-Pj7">
<rect key="frame" x="-1" y="137.5" width="266" height="16"/>
<rect key="frame" x="-1" y="160.5" width="266" height="16"/>
<buttonCell key="cell" type="check" title="Use ESC key to clear the entire input buffer" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="f2j-xD-4xK">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -388,7 +389,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mzw-F2-aAQ">
<rect key="frame" x="-1" y="114.5" width="295" height="16"/>
<rect key="frame" x="-1" y="137.5" width="295" height="16"/>
<buttonCell key="cell" type="check" title="Emulating select-candidate-per-character mode" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="ArK-Vk-OoT">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -398,7 +399,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="j8R-Hj-3dj">
<rect key="frame" x="-1" y="91.5" width="340" height="16"/>
<rect key="frame" x="-1" y="114.5" width="340" height="16"/>
<buttonCell key="cell" type="check" title="Automatically correct reading combinations when typing" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="chkAutoCorrectReadingCombination">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -408,7 +409,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="6MM-WC-Mpd">
<rect key="frame" x="-1" y="68.5" width="381" height="16"/>
<rect key="frame" x="-1" y="91.5" width="381" height="16"/>
<buttonCell key="cell" type="check" title="Allow using Enter key to confirm associated candidate selection" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="chkAlsoConfirmAssociatedCandidatesByEnter">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -418,7 +419,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HaB-rc-AcW">
<rect key="frame" x="-1" y="45.5" width="295" height="16"/>
<rect key="frame" x="-1" y="68.5" width="295" height="16"/>
<buttonCell key="cell" type="check" title="Allow backspace-editing miscomposed readings" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="chkKeepReadingUponCompositionError">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -428,7 +429,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xe6-Pu-3Fa">
<rect key="frame" x="-1" y="22.5" width="223" height="16"/>
<rect key="frame" x="-1" y="45.5" width="223" height="16"/>
<buttonCell key="cell" type="check" title="Trim unfinished readings on commit" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="tglTrimUnfinishedReadingsOnCommit">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -438,7 +439,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7QM-7z-tpq">
<rect key="frame" x="-1" y="-0.5" width="233" height="16"/>
<rect key="frame" x="-1" y="22.5" width="233" height="16"/>
<buttonCell key="cell" type="check" title="Always show tooltip texts horizontally" bezelStyle="regularSquare" imagePosition="left" controlSize="small" inset="2" id="lblAlwaysShowTooltipTextsHorizontally">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
@ -447,6 +448,23 @@
<binding destination="32" name="value" keyPath="values.AlwaysShowTooltipTextsHorizontally" id="szi-4g-EIC"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="233">
<rect key="frame" x="-1" y="-0.5" width="271" height="16"/>
<buttonCell key="cell" type="check" title="Show notifications when toggling Caps Lock" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="shc-Nu-UsM">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
<connections>
<binding destination="32" name="value" keyPath="values.ShowNotificationsWhenTogglingCapsLock" id="0e1-3G-eIc"/>
</connections>
</buttonCell>
<connections>
<binding destination="32" name="enabled" keyPath="values.UseIMKCandidateWindow" id="pHT-37-Eyh">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
@ -457,6 +475,7 @@
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
@ -467,6 +486,7 @@
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
@ -496,7 +516,7 @@
<constraint firstItem="qDj-B9-mZf" firstAttribute="top" secondItem="XWo-36-xGi" secondAttribute="top" constant="20" symbolic="YES" id="EmH-pX-s2C"/>
<constraint firstItem="qDj-B9-mZf" firstAttribute="leading" secondItem="XWo-36-xGi" secondAttribute="leading" constant="20" symbolic="YES" id="cs3-Kh-czM"/>
</constraints>
<point key="canvasLocation" x="-1091" y="-192"/>
<point key="canvasLocation" x="-1091" y="-181"/>
</visualEffectView>
<visualEffectView blendingMode="behindWindow" material="sidebar" state="followsWindowActiveState" id="BUt-lg-GPp" userLabel="vwrGeneral">
<rect key="frame" x="0.0" y="0.0" width="445" height="431"/>
@ -511,7 +531,6 @@
<constraint firstItem="Agg-8d-6bO" firstAttribute="top" secondItem="gZ0-OK-r7a" secondAttribute="bottom" constant="9.5" id="bzP-jz-kHQ"/>
<constraint firstItem="5Rz-c8-hp9" firstAttribute="top" secondItem="90" secondAttribute="bottom" constant="9" id="d7i-nb-E1l"/>
<constraint firstItem="28" firstAttribute="top" secondItem="Ldp-U1-36g" secondAttribute="bottom" constant="12" id="mas-hn-K8C"/>
<constraint firstItem="da1-7e-els" firstAttribute="top" secondItem="233" secondAttribute="bottom" constant="9.5" id="rca-Fa-YX4"/>
<constraint firstItem="da1-7e-els" firstAttribute="top" secondItem="Tax-1R-BrU" secondAttribute="top" constant="177" id="vTm-gq-EBY"/>
</constraints>
<rows>
@ -635,7 +654,7 @@
</gridCell>
<gridCell row="oKH-BK-XYd" column="X1u-rr-FFP" headOfMergedCell="ytZ-JE-JBZ" id="ytZ-JE-JBZ">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="749" translatesAutoresizingMaskIntoConstraints="NO" id="Yl4-Ar-L6r">
<rect key="frame" x="-2" y="252" width="314" height="15"/>
<rect key="frame" x="-2" y="244" width="314" height="15"/>
<constraints>
<constraint firstAttribute="height" constant="15" id="Bgf-Qu-fAR"/>
</constraints>
@ -649,7 +668,7 @@
<gridCell row="oKH-BK-XYd" column="s2d-Vu-rHv" headOfMergedCell="ytZ-JE-JBZ" id="R5f-hg-7u0"/>
<gridCell row="KfK-kF-hJJ" column="X1u-rr-FFP" xPlacement="trailing" yPlacement="top" rowAlignment="none" id="K38-CT-HsS">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="23">
<rect key="frame" x="9" y="228" width="131" height="15"/>
<rect key="frame" x="9" y="212" width="131" height="15"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Candidate List Layout:" id="24">
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -659,7 +678,7 @@
</gridCell>
<gridCell row="KfK-kF-hJJ" column="s2d-Vu-rHv" yPlacement="top" rowAlignment="none" id="ie1-hW-RKb">
<matrix key="contentView" wantsLayer="YES" verticalHuggingPriority="750" allowsEmptySelection="NO" translatesAutoresizingMaskIntoConstraints="NO" id="19">
<rect key="frame" x="144" y="228" width="240" height="15"/>
<rect key="frame" x="144" y="212" width="240" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="gpb-Gt-QDY"/>
<constraint firstAttribute="height" constant="15" id="wB2-bw-gqA"/>
@ -692,25 +711,7 @@
</matrix>
</gridCell>
<gridCell row="f3d-br-SXh" column="X1u-rr-FFP" id="HUa-vb-9P8"/>
<gridCell row="f3d-br-SXh" column="s2d-Vu-rHv" yPlacement="center" id="vxY-vh-tw7">
<button key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="233">
<rect key="frame" x="143" y="203.5" width="223" height="16"/>
<buttonCell key="cell" type="check" title="Show page buttons in candidate list" bezelStyle="regularSquare" imagePosition="left" controlSize="small" state="on" inset="2" id="shc-Nu-UsM">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/>
<connections>
<binding destination="32" name="value" keyPath="values.ShowPageButtonsInCandidateWindow" id="6BK-hU-lqs"/>
</connections>
</buttonCell>
<connections>
<binding destination="32" name="enabled" keyPath="values.UseIMKCandidateWindow" id="pHT-37-Eyh">
<dictionary key="options">
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
</connections>
</button>
</gridCell>
<gridCell row="f3d-br-SXh" column="s2d-Vu-rHv" yPlacement="center" id="vxY-vh-tw7"/>
<gridCell row="VsE-7i-d0I" column="X1u-rr-FFP" headOfMergedCell="1LR-8q-4K4" rowAlignment="none" id="1LR-8q-4K4">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="da1-7e-els">
<rect key="frame" x="-2" y="179" width="404" height="15"/>

View File

@ -90,7 +90,7 @@
"RUG-ls-KyA.title" = "Push the cursor in front of the phrase after selection";
"rVQ-Hx-cGi.title" = "Japanese";
"s7u-Fm-dVg.title" = "Cycling Pages";
"shc-Nu-UsM.title" = "Show page buttons in candidate list";
"shc-Nu-UsM.title" = "Show notifications when toggling Caps Lock";
"tglDevZoneIMKCandidate.title" = "Use IMK Candidate Window instead of Tadokoro (will reboot the IME)";
"tglTrimUnfinishedReadingsOnCommit.title" = "Trim unfinished readings on commit";
"TXr-FF-ehw.title" = "Traditional Chinese";

View File

@ -90,7 +90,7 @@
"RUG-ls-KyA.title" = "候補選択の直後、すぐカーソルを単語の向こうに推す";
"rVQ-Hx-cGi.title" = "和語";
"s7u-Fm-dVg.title" = "ページ";
"shc-Nu-UsM.title" = "ページボタンを表示";
"shc-Nu-UsM.title" = "Caps Lock で切り替えの時に吹出通知メッセージを";
"tglDevZoneIMKCandidate.title" = "IMK 候補陳列ウィンドウを起用(入力アプリは自動的に再起動)";
"tglTrimUnfinishedReadingsOnCommit.title" = "送り出す緩衝列内容から未完成な音読みを除く";
"TXr-FF-ehw.title" = "繁体中国語";

View File

@ -90,7 +90,7 @@
"RUG-ls-KyA.title" = "在选字后将光标置于该字词的前方";
"rVQ-Hx-cGi.title" = "和语";
"s7u-Fm-dVg.title" = "轮替页面";
"shc-Nu-UsM.title" = "在选字窗内显示翻页按钮";
"shc-Nu-UsM.title" = "以 Caps Lock 切换输入法/中英模式时显示通知";
"tglDevZoneIMKCandidate.title" = "启用与 macOS 内建输入法相同的 IMK 选字窗(会自动重启输入法)";
"tglTrimUnfinishedReadingsOnCommit.title" = "在递交时清理未完成拼写的读音";
"TXr-FF-ehw.title" = "繁体中文";

View File

@ -90,7 +90,7 @@
"RUG-ls-KyA.title" = "在選字後將游標置於該字詞的前方";
"rVQ-Hx-cGi.title" = "和語";
"s7u-Fm-dVg.title" = "輪替頁面";
"shc-Nu-UsM.title" = "在選字窗內顯示翻頁按鈕";
"shc-Nu-UsM.title" = "以 Caps Lock 切換輸入法/中英模式時顯示通知";
"tglDevZoneIMKCandidate.title" = "啟用與 macOS 內建輸入法相同的 IMK 選字窗(會自動重啟輸入法)";
"tglTrimUnfinishedReadingsOnCommit.title" = "在遞交時清理未完成拼寫的讀音";
"TXr-FF-ehw.title" = "繁體中文";

View File

@ -5,7 +5,7 @@
<key>CFBundleShortVersionString</key>
<string>2.8.0</string>
<key>CFBundleVersion</key>
<string>2800</string>
<string>2802</string>
<key>UpdateInfoEndpoint</key>
<string>https://gitee.com/vchewing/vChewing-macOS/raw/main/Update-Info.plist</string>
<key>UpdateInfoSite</key>

View File

@ -1262,7 +1262,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
@ -1302,7 +1302,7 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@ -1341,7 +1341,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
@ -1394,7 +1394,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;
@ -1529,7 +1529,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
@ -1590,7 +1590,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = "";
@ -1638,7 +1638,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
@ -1684,7 +1684,7 @@
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2800;
CURRENT_PROJECT_VERSION = 2802;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;