Repo // Implementing reverse-lookup, requiring macOS 10.15 and later.
- Prefs & PrefUI // Add reverse-lookup toggle.
This commit is contained in:
parent
6e67a5edc8
commit
f94ecf5fda
|
@ -14,6 +14,7 @@ open class CtlCandidate: NSWindowController, CtlCandidateProtocol {
|
|||
open var currentLayout: NSUserInterfaceLayoutOrientation = .horizontal
|
||||
open var locale: String = ""
|
||||
open var useLangIdentifier: Bool = false
|
||||
open var reverseLookupResult: String = ""
|
||||
|
||||
open func highlightedColor() -> NSColor {
|
||||
var result = NSColor.alternateSelectedControlColor
|
||||
|
|
|
@ -19,20 +19,32 @@ public class CtlCandidateTDK: CtlCandidate {
|
|||
|
||||
@available(macOS 12, *)
|
||||
public var theViewHorizontal: VwrCandidateHorizontal {
|
||||
.init(controller: self, thePool: thePoolHorizontal, tooltip: tooltip)
|
||||
.init(
|
||||
controller: self, thePool: thePoolHorizontal,
|
||||
tooltip: tooltip, reverseLookupResult: reverseLookupResult
|
||||
)
|
||||
}
|
||||
|
||||
@available(macOS 12, *)
|
||||
public var theViewVertical: VwrCandidateVertical {
|
||||
.init(controller: self, thePool: thePoolVertical, tooltip: tooltip)
|
||||
.init(
|
||||
controller: self, thePool: thePoolVertical,
|
||||
tooltip: tooltip, reverseLookupResult: reverseLookupResult
|
||||
)
|
||||
}
|
||||
|
||||
public var theViewHorizontalBackports: VwrCandidateHorizontalBackports {
|
||||
.init(controller: self, thePool: thePoolHorizontal, tooltip: tooltip)
|
||||
.init(
|
||||
controller: self, thePool: thePoolHorizontal,
|
||||
tooltip: tooltip, reverseLookupResult: reverseLookupResult
|
||||
)
|
||||
}
|
||||
|
||||
public var theViewVerticalBackports: VwrCandidateVerticalBackports {
|
||||
.init(controller: self, thePool: thePoolVertical, tooltip: tooltip)
|
||||
.init(
|
||||
controller: self, thePool: thePoolVertical,
|
||||
tooltip: tooltip, reverseLookupResult: reverseLookupResult
|
||||
)
|
||||
}
|
||||
|
||||
public var thePool: CandidatePool {
|
||||
|
@ -105,6 +117,7 @@ public class CtlCandidateTDK: CtlCandidate {
|
|||
|
||||
override open func updateDisplay() {
|
||||
guard let window = window else { return }
|
||||
reverseLookupResult = delegate?.annotate(for: currentSelectedCandidateText) ?? ""
|
||||
switch currentLayout {
|
||||
case .horizontal:
|
||||
DispatchQueue.main.async { [self] in
|
||||
|
@ -223,6 +236,13 @@ extension CtlCandidateTDK {
|
|||
if #unavailable(macOS 12) { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
private var currentSelectedCandidateText: String {
|
||||
if thePool.candidateDataAll.count > highlightedIndex {
|
||||
return thePool.candidateDataAll[highlightedIndex].displayedText
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.15, *)
|
||||
|
|
|
@ -38,6 +38,7 @@ public struct VwrCandidateHorizontal: View {
|
|||
public var controller: CtlCandidateTDK
|
||||
@State public var thePool: CandidatePool
|
||||
@State public var tooltip: String = ""
|
||||
@State public var reverseLookupResult: String = ""
|
||||
|
||||
private var positionLabel: String {
|
||||
(thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description
|
||||
|
@ -91,16 +92,21 @@ public struct VwrCandidateHorizontal: View {
|
|||
}
|
||||
.fixedSize(horizontal: false, vertical: true).padding(5)
|
||||
.background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea())
|
||||
ZStack(alignment: .leading) {
|
||||
ZStack(alignment: .trailing) {
|
||||
Color(nsColor: tooltip.isEmpty ? .windowBackgroundColor : CandidateCellData.highlightBackground)
|
||||
.ignoresSafeArea()
|
||||
HStack(alignment: .bottom) {
|
||||
Text(tooltip).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)
|
||||
if !tooltip.isEmpty {
|
||||
Text(tooltip).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
if !reverseLookupResult.isEmpty, !(controller.delegate?.isVerticalTyping ?? true) {
|
||||
Text(reverseLookupResult).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
Text(positionLabel).lineLimit(1)
|
||||
}
|
||||
.font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
|
||||
.padding(7).foregroundColor(
|
||||
.init(nsColor: tooltip.isEmpty ? .controlTextColor : .selectedMenuItemTextColor.withAlphaComponent(0.9))
|
||||
)
|
||||
|
|
|
@ -37,6 +37,7 @@ public struct VwrCandidateVertical: View {
|
|||
public var controller: CtlCandidateTDK
|
||||
@State public var thePool: CandidatePool
|
||||
@State public var tooltip: String = ""
|
||||
@State public var reverseLookupResult: String = ""
|
||||
|
||||
private var positionLabel: String {
|
||||
(thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description
|
||||
|
@ -99,20 +100,26 @@ public struct VwrCandidateVertical: View {
|
|||
}
|
||||
.fixedSize(horizontal: true, vertical: false).padding(5)
|
||||
.background(Color(nsColor: NSColor.controlBackgroundColor).ignoresSafeArea())
|
||||
ZStack(alignment: .leading) {
|
||||
ZStack(alignment: .trailing) {
|
||||
Color(nsColor: tooltip.isEmpty ? .windowBackgroundColor : CandidateCellData.highlightBackground)
|
||||
.ignoresSafeArea()
|
||||
HStack(alignment: .bottom) {
|
||||
Text(tooltip).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)
|
||||
if !tooltip.isEmpty {
|
||||
Text(tooltip).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
if !reverseLookupResult.isEmpty, !(controller.delegate?.isVerticalTyping ?? true) {
|
||||
Text(reverseLookupResult).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
Text(positionLabel).lineLimit(1)
|
||||
}
|
||||
.font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
|
||||
.padding(7).foregroundColor(
|
||||
.init(nsColor: tooltip.isEmpty ? .controlTextColor : .selectedMenuItemTextColor.withAlphaComponent(0.9))
|
||||
)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10).stroke(.white.opacity(0.2), lineWidth: 1)
|
||||
|
|
|
@ -40,6 +40,7 @@ public struct VwrCandidateHorizontalBackports: View {
|
|||
public var controller: CtlCandidateTDK
|
||||
@State public var thePool: CandidatePool
|
||||
@State public var tooltip: String = ""
|
||||
@State public var reverseLookupResult: String = ""
|
||||
|
||||
private var positionLabel: String {
|
||||
(thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description
|
||||
|
@ -93,7 +94,7 @@ public struct VwrCandidateHorizontalBackports: View {
|
|||
}
|
||||
.fixedSize(horizontal: false, vertical: true).padding(5)
|
||||
.background(Color(white: colorScheme == .dark ? 0.1 : 1))
|
||||
ZStack(alignment: .leading) {
|
||||
ZStack(alignment: .trailing) {
|
||||
if tooltip.isEmpty {
|
||||
Color(white: colorScheme == .dark ? 0.2 : 0.9)
|
||||
} else {
|
||||
|
@ -101,12 +102,17 @@ public struct VwrCandidateHorizontalBackports: View {
|
|||
controller.highlightedColorUIBackports
|
||||
}
|
||||
HStack(alignment: .bottom) {
|
||||
Text(tooltip).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)
|
||||
if !tooltip.isEmpty {
|
||||
Text(tooltip).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
if !reverseLookupResult.isEmpty, !(controller.delegate?.isVerticalTyping ?? true) {
|
||||
Text(reverseLookupResult).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
Text(positionLabel).lineLimit(1)
|
||||
}
|
||||
.font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
|
||||
.padding(7).foregroundColor(
|
||||
tooltip.isEmpty && colorScheme == .light ? Color(white: 0.1) : Color(white: 0.9)
|
||||
)
|
||||
|
|
|
@ -39,6 +39,7 @@ public struct VwrCandidateVerticalBackports: View {
|
|||
public var controller: CtlCandidateTDK
|
||||
@State public var thePool: CandidatePool
|
||||
@State public var tooltip: String = ""
|
||||
@State public var reverseLookupResult: String = ""
|
||||
|
||||
private var positionLabel: String {
|
||||
(thePool.highlightedIndex + 1).description + "/" + thePool.candidateDataAll.count.description
|
||||
|
@ -101,7 +102,7 @@ public struct VwrCandidateVerticalBackports: View {
|
|||
}
|
||||
.fixedSize(horizontal: true, vertical: false).padding(5)
|
||||
.background(Color(white: colorScheme == .dark ? 0.1 : 1))
|
||||
ZStack(alignment: .leading) {
|
||||
ZStack(alignment: .trailing) {
|
||||
if tooltip.isEmpty {
|
||||
Color(white: colorScheme == .dark ? 0.2 : 0.9)
|
||||
} else {
|
||||
|
@ -109,16 +110,22 @@ public struct VwrCandidateVerticalBackports: View {
|
|||
controller.highlightedColorUIBackports
|
||||
}
|
||||
HStack(alignment: .bottom) {
|
||||
Text(tooltip).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)
|
||||
if !tooltip.isEmpty {
|
||||
Text(tooltip).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
if !reverseLookupResult.isEmpty, !(controller.delegate?.isVerticalTyping ?? true) {
|
||||
Text(reverseLookupResult).lineLimit(1)
|
||||
Spacer()
|
||||
}
|
||||
Text(positionLabel).lineLimit(1)
|
||||
}
|
||||
.font(.system(size: max(CandidateCellData.unifiedSize * 0.7, 11), weight: .bold))
|
||||
.padding(7).foregroundColor(
|
||||
tooltip.isEmpty && colorScheme == .light ? Color(white: 0.1) : Color(white: 0.9)
|
||||
)
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 10).stroke(.white.opacity(0.2), lineWidth: 1)
|
||||
|
|
|
@ -27,6 +27,7 @@ extension vChewingLM {
|
|||
public private(set) var keyNameMap: [String: String] = [:]
|
||||
public private(set) var charDefMap: [String: [String]] = [:]
|
||||
public private(set) var charDefWildcardMap: [String: [String]] = [:]
|
||||
public private(set) var reverseLookupMap: [String: [String]] = [:]
|
||||
/// 字根輸入法專用八股文:[字詞:頻次]。
|
||||
public private(set) var octagramMap: [String: Int] = [:]
|
||||
/// 音韻輸入法專用八股文:[字詞:(頻次, 讀音)]。
|
||||
|
@ -91,13 +92,15 @@ extension vChewingLM {
|
|||
if loadingKeys, !cells[0].contains("%keyname") {
|
||||
keyNameMap[strFirstCell] = String(cells[1])
|
||||
} else if loadingCharDefinitions, !strLine.contains("%chardef") {
|
||||
let strSecondCell = String(cells[1])
|
||||
theMaxKeyLength = max(theMaxKeyLength, cells[0].count)
|
||||
charDefMap[strFirstCell, default: []].append(String(cells[1]))
|
||||
charDefMap[strFirstCell, default: []].append(strSecondCell)
|
||||
reverseLookupMap[strSecondCell, default: []].append(strFirstCell)
|
||||
var keyComps = strFirstCell.charComponents
|
||||
while !keyComps.isEmpty, !wildcardKey.isEmpty {
|
||||
keyComps.removeLast()
|
||||
if !wildcardKey.isEmpty {
|
||||
charDefWildcardMap[keyComps.joined() + wildcardKey, default: []].append(String(cells[1]))
|
||||
charDefWildcardMap[keyComps.joined() + wildcardKey, default: []].append(strSecondCell)
|
||||
}
|
||||
}
|
||||
} else if loadingOctagramData, !strLine.contains("%octagram") {
|
||||
|
|
|
@ -12,11 +12,14 @@ public protocol CtlCandidateDelegate {
|
|||
func candidatePairs(conv: Bool) -> [(String, String)]
|
||||
func candidatePairSelected(at index: Int)
|
||||
func candidates(_ sender: Any!) -> [Any]!
|
||||
@discardableResult func annotate(for value: String) -> String
|
||||
var selectionKeys: String { get }
|
||||
var isVerticalTyping: Bool { get }
|
||||
}
|
||||
|
||||
public protocol CtlCandidateProtocol {
|
||||
var tooltip: String { get set }
|
||||
var reverseLookupResult: String { get set }
|
||||
var locale: String { get set }
|
||||
var currentLayout: NSUserInterfaceLayoutOrientation { get set }
|
||||
var delegate: CtlCandidateDelegate? { get set }
|
||||
|
|
|
@ -51,6 +51,7 @@ public protocol PrefMgrProtocol {
|
|||
var inlineDumpPinyinInLieuOfZhuyin: Bool { get set }
|
||||
var showTranslatedStrokesInCompositionBuffer: Bool { get set }
|
||||
var forceCassetteChineseConversion: Int { get set }
|
||||
var showReverseLookupInCandidateUI: Bool { get set }
|
||||
var cns11643Enabled: Bool { get set }
|
||||
var cassetteEnabled: Bool { get set }
|
||||
var symbolInputEnabled: Bool { get set }
|
||||
|
|
|
@ -64,6 +64,7 @@ public enum UserDef: String, CaseIterable {
|
|||
case kOnlyLoadFactoryLangModelsIfNeeded = "OnlyLoadFactoryLangModelsIfNeeded"
|
||||
case kShowTranslatedStrokesInCompositionBuffer = "ShowTranslatedStrokesInCompositionBuffer"
|
||||
case kForceCassetteChineseConversion = "ForceCassetteChineseConversion"
|
||||
case kShowReverseLookupInCandidateUI = "ShowReverseLookupInCandidateUI"
|
||||
|
||||
case kUseIMKCandidateWindow = "UseIMKCandidateWindow"
|
||||
case kHandleDefaultCandidateFontsByLangIdentifier = "HandleDefaultCandidateFontsByLangIdentifier"
|
||||
|
|
|
@ -150,6 +150,9 @@ public class PrefMgr: PrefMgrProtocol {
|
|||
@AppProperty(key: UserDef.kForceCassetteChineseConversion.rawValue, defaultValue: 0)
|
||||
public var forceCassetteChineseConversion: Int
|
||||
|
||||
@AppProperty(key: UserDef.kShowReverseLookupInCandidateUI.rawValue, defaultValue: true)
|
||||
public var showReverseLookupInCandidateUI: Bool
|
||||
|
||||
// MARK: - Settings (Tier 2)
|
||||
|
||||
@AppProperty(key: UserDef.kUseIMKCandidateWindow.rawValue, defaultValue: false)
|
||||
|
|
|
@ -19,6 +19,7 @@ extension PrefMgr {
|
|||
shiftKeyAccommodationBehavior = 0
|
||||
disableShiftTogglingAlphanumericalMode = true
|
||||
togglingAlphanumericalModeWithLShift = false
|
||||
showReverseLookupInCandidateUI = false
|
||||
}
|
||||
// 自動糾正選字鍵 (利用其 didSet 特性)
|
||||
candidateKeys = candidateKeys
|
||||
|
|
|
@ -56,6 +56,16 @@ extension SessionCtl: InputHandlerDelegate {
|
|||
// MARK: - Candidate Controller Delegate
|
||||
|
||||
extension SessionCtl: CtlCandidateDelegate {
|
||||
@discardableResult public func annotate(for value: String) -> String {
|
||||
// 這一段專門處理「反查」。
|
||||
if !PrefMgr.shared.showReverseLookupInCandidateUI { return "" }
|
||||
if isVerticalTyping { return "" } // 縱排輸入的場合,選字窗沒那個空間顯示反查結果。
|
||||
if value.isEmpty { return "" } // 空字串沒有需要反查的東西。
|
||||
if value.contains("_") { return "" }
|
||||
guard let lookupResult = LMMgr.currentLM.currentCassette.reverseLookupMap[value] else { return "" }
|
||||
return lookupResult.joined(separator: " ")
|
||||
}
|
||||
|
||||
public var selectionKeys: String {
|
||||
PrefMgr.shared.useIMKCandidateWindow ? "123456789" : PrefMgr.shared.candidateKeys
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import Shared
|
|||
/// 威注音自用的 IMKCandidates 型別。因為有用到 bridging header,所以無法弄成 Swift Package。
|
||||
public class CtlCandidateIMK: IMKCandidates, CtlCandidateProtocol {
|
||||
public var tooltip: String = ""
|
||||
public var reverseLookupResult: String = ""
|
||||
public var locale: String = ""
|
||||
public var useLangIdentifier: Bool = false
|
||||
public var currentLayout: NSUserInterfaceLayoutOrientation = .horizontal
|
||||
|
|
|
@ -36,6 +36,8 @@ struct VwrPrefPaneGeneral: View {
|
|||
@State private var selEnableAutoUpdateCheck = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kCheckUpdateAutomatically.rawValue)
|
||||
@State private var selEnableDebugMode = UserDefaults.standard.bool(forKey: UserDef.kIsDebugModeEnabled.rawValue)
|
||||
@State private var selShowReverseLookupInCandidateUI = UserDefaults.standard.bool(
|
||||
forKey: UserDef.kShowReverseLookupInCandidateUI.rawValue)
|
||||
|
||||
private let contentMaxHeight: Double = 440
|
||||
private let contentWidth: Double = {
|
||||
|
@ -129,6 +131,14 @@ struct VwrPrefPaneGeneral: View {
|
|||
.pickerStyle(RadioGroupPickerStyle())
|
||||
Text(LocalizedStringKey("Choose your preferred layout of the candidate window."))
|
||||
.preferenceDescription()
|
||||
Toggle(
|
||||
LocalizedStringKey("Show available reverse-lookup results in candidate window"),
|
||||
isOn: $selShowReverseLookupInCandidateUI.onChange {
|
||||
PrefMgr.shared.showReverseLookupInCandidateUI = selShowReverseLookupInCandidateUI
|
||||
}
|
||||
)
|
||||
.controlSize(.small)
|
||||
.disabled(PrefMgr.shared.useIMKCandidateWindow)
|
||||
}
|
||||
SSPreferences.Section(label: { Text(LocalizedStringKey("Output Settings:")) }) {
|
||||
Toggle(
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
"Selection Keys:" = "Selection Keys:";
|
||||
"Shift+BackSpace:" = "Shift+BackSpace:";
|
||||
"Shift+Letter:" = "Shift+Letter:";
|
||||
"Show available reverse-lookup results in candidate window" = "Show available reverse-lookup results in candidate window";
|
||||
"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer";
|
||||
"Show notifications when toggling Caps Lock" = "Show notifications when toggling Caps Lock";
|
||||
"Show translated strokes in composition buffer" = "Show translated strokes in composition buffer";
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
"Selection Keys:" = "Selection Keys:";
|
||||
"Shift+BackSpace:" = "Shift+BackSpace:";
|
||||
"Shift+Letter:" = "Shift+Letter:";
|
||||
"Show available reverse-lookup results in candidate window" = "Show available reverse-lookup results in candidate window";
|
||||
"Show Hanyu-Pinyin in the inline composition buffer" = "Show Hanyu-Pinyin in the inline composition buffer";
|
||||
"Show notifications when toggling Caps Lock" = "Show notifications when toggling Caps Lock";
|
||||
"Show translated strokes in composition buffer" = "Show translated strokes in composition buffer";
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
"Selection Keys:" = "言選り用キー:";
|
||||
"Shift+BackSpace:" = "Shift+BackSpace:";
|
||||
"Shift+Letter:" = "Shift+文字キー:";
|
||||
"Show available reverse-lookup results in candidate window" = "候補陳列ウィンドウで可能な逆引参照結果を示す";
|
||||
"Show Hanyu-Pinyin in the inline composition buffer" = "弁音合併入力(入力緩衝列で音読みを漢語弁音に)";
|
||||
"Show notifications when toggling Caps Lock" = "Caps Lock で切り替えの時に吹出通知メッセージを";
|
||||
"Show translated strokes in composition buffer" = "原始キーネームでなく、筆画を入力緩衝列で表示する";
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
"Selection Keys:" = "选字键:";
|
||||
"Shift+BackSpace:" = "Shift+退格键:";
|
||||
"Shift+Letter:" = "Shift+字母键:";
|
||||
"Show available reverse-lookup results in candidate window" = "在选字窗内显示可用的字根反查结果";
|
||||
"Show Hanyu-Pinyin in the inline composition buffer" = "拼音并击(组字区内显示汉语拼音)";
|
||||
"Show notifications when toggling Caps Lock" = "以 Caps Lock 切换输入法/中英模式时显示通知";
|
||||
"Show translated strokes in composition buffer" = "在组字区内显示字根、而非原始键盘码";
|
||||
|
|
|
@ -223,6 +223,7 @@
|
|||
"Selection Keys:" = "選字鍵:";
|
||||
"Shift+BackSpace:" = "Shift+退格鍵:";
|
||||
"Shift+Letter:" = "Shift+字母鍵:";
|
||||
"Show available reverse-lookup results in candidate window" = "在選字窗內顯示可用的字根反查結果";
|
||||
"Show Hanyu-Pinyin in the inline composition buffer" = "拼音並擊(組字區內顯示漢語拼音)";
|
||||
"Show notifications when toggling Caps Lock" = "以 Caps Lock 切換輸入法/中英模式時顯示通知";
|
||||
"Show translated strokes in composition buffer" = "在組字區內顯示字根、而非原始鍵盤碼";
|
||||
|
|
Loading…
Reference in New Issue