SettingsUI // Massive refactor using UserDefRenderable.

This commit is contained in:
ShikiSuen 2024-01-28 16:26:26 +08:00
parent 09aec2bb06
commit 01924a1cf1
8 changed files with 172 additions and 728 deletions

View File

@ -65,207 +65,61 @@ public struct VwrSettingsPaneBehavior: View {
@AppStorage(wrappedValue: false, UserDef.kBypassNonAppleCapsLockHandling.rawValue)
public dynamic var bypassNonAppleCapsLockHandling: Bool
var macOSMontereyOrLaterDetected: Bool { true } // Always met.
// MARK: - Main View
public var body: some View {
ScrollView {
Form {
Section {
UserDef.kChooseCandidateUsingSpace.bind($chooseCandidateUsingSpace).render()
UserDef.kEscToCleanInputBuffer.bind($escToCleanInputBuffer).render()
UserDef.kAlsoConfirmAssociatedCandidatesByEnter.bind($alsoConfirmAssociatedCandidatesByEnter).render()
UserDef.kSpecifyShiftBackSpaceKeyBehavior.bind($specifyShiftBackSpaceKeyBehavior).render()
UserDef.kSpecifyShiftTabKeyBehavior.bind($specifyShiftTabKeyBehavior).render()
.pickerStyle(RadioGroupPickerStyle())
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Enable Space key for calling candidate window"),
isOn: $chooseCandidateUsingSpace
)
Text(
LocalizedStringKey(
"If disabled, this will insert space instead."
)
)
.settingsDescription()
UserDef.kSpecifyShiftSpaceKeyBehavior.bind($specifyShiftSpaceKeyBehavior).render()
UserDef.kUseSpaceToCommitHighlightedSCPCCandidate.bind($useSpaceToCommitHighlightedSCPCCandidate).render()
}
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Use ESC key to clear the entire input buffer"),
isOn: $escToCleanInputBuffer
)
Text(
LocalizedStringKey(
"If unchecked, the ESC key will try cleaning the unfinished readings / strokes first, and will commit the current composition buffer if there's no unfinished readings / strokes."
)
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Allow using Enter key to confirm associated candidate selection"),
isOn: $alsoConfirmAssociatedCandidatesByEnter
)
Text(
LocalizedStringKey(
"Otherwise, only the candidate keys are allowed to confirm associates."
)
)
.settingsDescription()
}
VStack(alignment: .leading) {
Picker(
"Shift+BackSpace:",
selection: $specifyShiftBackSpaceKeyBehavior
) {
Text(LocalizedStringKey("Disassemble the previous reading, dropping its intonation")).tag(0)
Text(LocalizedStringKey("Clear the entire inline composition buffer like Shift+Delete")).tag(1)
Text(LocalizedStringKey("Always drop the previous reading")).tag(2)
}
Text(LocalizedStringKey("Disassembling process does not work with non-phonetic reading keys."))
.settingsDescription()
}
VStack(alignment: .leading) {
Picker(
"(Shift+)Tab:",
selection: $specifyShiftTabKeyBehavior
) {
Text(LocalizedStringKey("for revolving candidates")).tag(false)
Text(LocalizedStringKey("for revolving pages")).tag(true)
}
.pickerStyle(RadioGroupPickerStyle())
Text(LocalizedStringKey("Choose the behavior of (Shift+)Tab key in the candidate window."))
.settingsDescription()
}
VStack(alignment: .leading) {
Picker(
"(Shift+)Space:",
selection: $specifyShiftSpaceKeyBehavior
) {
Text(LocalizedStringKey("Space to +revolve candidates, Shift+Space to +revolve pages")).tag(false)
Text(LocalizedStringKey("Space to +revolve pages, Shift+Space to +revolve candidates")).tag(true)
}
Spacer()
Text(LocalizedStringKey("Choose the behavior of (Shift+)Space key with candidates."))
.settingsDescription()
Toggle(
LocalizedStringKey("Use Space to confirm highlighted candidate in Per-Char Select Mode"),
isOn: $useSpaceToCommitHighlightedSCPCCandidate
)
}
}
Section {
VStack(alignment: .leading) {
Picker(
"Shift+Letter:",
selection: $upperCaseLetterKeyBehavior
) {
Text(LocalizedStringKey("Type them into inline composition buffer")).tag(0)
Text(LocalizedStringKey("Always directly commit lowercased letters")).tag(1)
Text(LocalizedStringKey("Always directly commit uppercased letters")).tag(2)
Text(LocalizedStringKey("Directly commit lowercased letters only if the compositor is empty")).tag(3)
Text(LocalizedStringKey("Directly commit uppercased letters only if the compositor is empty")).tag(4)
}
Text(LocalizedStringKey("Choose the behavior of Shift+Letter key with letter inputs."))
.settingsDescription()
}
UserDef.kUpperCaseLetterKeyBehavior.bind($upperCaseLetterKeyBehavior).render()
}
Section {
VStack(alignment: .leading) {
Picker(
"Intonation Key:",
selection: $specifyIntonationKeyBehavior
) {
Text(LocalizedStringKey("Override the previous reading's intonation with candidate-reset")).tag(0)
Text(LocalizedStringKey("Only override the intonation of the previous reading if different")).tag(1)
Text(LocalizedStringKey("Always type intonations to the inline composition buffer")).tag(2)
}
Text(LocalizedStringKey("Specify the behavior of intonation key when syllable composer is empty."))
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Accept leading intonations in rare cases"),
isOn: $acceptLeadingIntonations
)
Spacer()
Text(LocalizedStringKey("This feature accommodates certain typing mistakes that the intonation mark might be typed at first (which is sequentially wrong from a common sense that intonation marks are supposed to be used for confirming combinations). It won't work if the current parser is of (any) pinyin. Also, this feature won't work when an intonation override is possible (and enabled)."))
.settingsDescription()
}
UserDef.kSpecifyIntonationKeyBehavior.bind($specifyIntonationKeyBehavior).render()
UserDef.kAcceptLeadingIntonations.bind($acceptLeadingIntonations).render()
}
Section {
UserDef.kBypassNonAppleCapsLockHandling.bind($bypassNonAppleCapsLockHandling).render()
UserDef.kShareAlphanumericalModeStatusAcrossClients.bind($shareAlphanumericalModeStatusAcrossClients).render()
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("i18n:UserDef.kBypassNonAppleCapsLockHandling.shortTitle"),
isOn: $bypassNonAppleCapsLockHandling
)
Text("i18n:UserDef.kBypassNonAppleCapsLockHandling.description")
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Share alphanumerical mode status across all clients"),
isOn: $shareAlphanumericalModeStatusAcrossClients
)
Text(
"This only works when being toggled by Shift key and JIS Eisu key.".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Toggle alphanumerical mode with Left-Shift"),
isOn: $togglingAlphanumericalModeWithLShift.onChange {
UserDef.kTogglingAlphanumericalModeWithLShift.bind(
$togglingAlphanumericalModeWithLShift.onChange {
SessionCtl.theShiftKeyDetector.toggleWithLShift = togglingAlphanumericalModeWithLShift
}
)
Toggle(
LocalizedStringKey("Toggle alphanumerical mode with Right-Shift"),
isOn: $togglingAlphanumericalModeWithRShift.onChange {
).render()
UserDef.kTogglingAlphanumericalModeWithRShift.bind(
$togglingAlphanumericalModeWithRShift.onChange {
SessionCtl.theShiftKeyDetector.toggleWithRShift = togglingAlphanumericalModeWithRShift
}
)
).render()
Spacer()
Text(
"This feature requires macOS 10.15 and above.".localized + CtlSettingsUI.sentenceSeparator
+ "i18n:settings.shiftKeyASCIITogle.description".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("i18n:UserDef.kShiftEisuToggleOffTogetherWithCapsLock.shortTitle"),
isOn: $shiftEisuToggleOffTogetherWithCapsLock
)
Spacer()
Text("i18n:UserDef.kShiftEisuToggleOffTogetherWithCapsLock.description")
.settingsDescription()
Group {
Text("") + Text(LocalizedStringKey("This feature requires macOS \("10.15") and above."))
+ Text(CtlSettingsUI.sentenceSeparator)
+ Text("i18n:settings.shiftKeyASCIITogle.description".localized)
}.settingsDescription()
}
UserDef.kShiftEisuToggleOffTogetherWithCapsLock.bind($shiftEisuToggleOffTogetherWithCapsLock).render()
}
Section {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Show notifications when toggling Caps Lock"),
isOn: $showNotificationsWhenTogglingCapsLock.onChange {
if !macOSMontereyOrLaterDetected, showNotificationsWhenTogglingCapsLock {
showNotificationsWhenTogglingCapsLock.toggle()
}
}
).disabled(!macOSMontereyOrLaterDetected)
Text(
"This feature requires macOS 12 and above.".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Always show tooltip texts horizontally"),
isOn: $alwaysShowTooltipTextsHorizontally
).disabled(Bundle.main.preferredLocalizations[0] == "en")
Text(
LocalizedStringKey(
"Key names in tooltip will be shown as symbols when the tooltip is vertical. However, this option will be ignored since tooltip will always be horizontal if the UI language is English."
)
)
.settingsDescription()
}
UserDef.kShowNotificationsWhenTogglingCapsLock.bind($showNotificationsWhenTogglingCapsLock).render()
UserDef.kAlwaysShowTooltipTextsHorizontally.bind($alwaysShowTooltipTextsHorizontally).render()
.disabled(Bundle.main.preferredLocalizations[0] == "en")
}
}.formStyled()
}

View File

@ -56,133 +56,37 @@ public struct VwrSettingsPaneCandidates: View {
ScrollView {
Form {
Section {
VStack(alignment: .leading) {
Picker(
"Cursor Selection:",
selection: $useRearCursorMode
) {
Text(LocalizedStringKey("in front of the phrase (like macOS built-in Zhuyin IME)")).tag(false)
Text(LocalizedStringKey("at the rear of the phrase (like Microsoft New Phonetic)")).tag(true)
}
Text(LocalizedStringKey("Choose the cursor position where you want to list possible candidates."))
.settingsDescription()
}
Toggle(
LocalizedStringKey("Push the cursor in front of the phrase after selection"),
isOn: $moveCursorAfterSelectingCandidate
)
UserDef.kUseRearCursorMode.bind($useRearCursorMode).render()
UserDef.kMoveCursorAfterSelectingCandidate.bind($moveCursorAfterSelectingCandidate).render()
if !useRearCursorMode {
Toggle(
LocalizedStringKey("Adjust candidate window location according to current node length"),
isOn: $useDynamicCandidateWindowOrigin
).disabled(useRearCursorMode)
UserDef.kUseDynamicCandidateWindowOrigin.bind($useDynamicCandidateWindowOrigin).render()
.disabled(useRearCursorMode)
}
}
Section {
VStack(alignment: .leading) { VwrSettingsPaneCandidates_SelectionKeys() }
VStack(alignment: .leading) {
Picker(
"Candidate Layout:",
selection: $useHorizontalCandidateList
) {
Text(LocalizedStringKey("Vertical")).tag(false)
Text(LocalizedStringKey("Horizontal")).tag(true)
}
VwrSettingsPaneCandidates_SelectionKeys()
UserDef.kUseHorizontalCandidateList.bind($useHorizontalCandidateList).render()
.pickerStyle(RadioGroupPickerStyle())
Text(LocalizedStringKey("Choose your preferred layout of the candidate window."))
.settingsDescription()
}
VStack(alignment: .leading) {
Picker(
"Candidate Size:",
selection: $candidateListTextSize.onChange {
UserDef.kCandidateListTextSize.bind(
$candidateListTextSize.onChange {
guard !(12 ... 196).contains(candidateListTextSize) else { return }
candidateListTextSize = max(12, min(candidateListTextSize, 196))
}
) {
Group {
Text("12").tag(12.0)
Text("14").tag(14.0)
Text("16").tag(16.0)
Text("17").tag(17.0)
Text("18").tag(18.0)
Text("20").tag(20.0)
Text("22").tag(22.0)
Text("24").tag(24.0)
}
Group {
Text("32").tag(32.0)
Text("64").tag(64.0)
Text("96").tag(96.0)
}
}
Text(LocalizedStringKey("Choose candidate font size for better visual clarity."))
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Use only one row / column in candidate window"),
isOn: $candidateWindowShowOnlyOneLine
)
Text(
"Tadokoro candidate window shows 4 rows / columns by default, providing similar experiences from Microsoft New Phonetic IME and macOS bult-in Chinese IME (since macOS 10.9). However, for some users who have presbyopia, they prefer giant candidate font sizes, resulting a concern that multiple rows / columns of candidates can make the candidate window looks too big, hence this option. Note that this option will be dismissed if the typing context is vertical, forcing the candidates to be shown in only one row / column. Only one reverse-lookup result can be made available in single row / column mode due to reduced candidate window size.".localized
)
.settingsDescription()
}
).render()
UserDef.kCandidateWindowShowOnlyOneLine.bind($candidateWindowShowOnlyOneLine).render()
if !candidateWindowShowOnlyOneLine {
Toggle(
LocalizedStringKey("Always expand candidate window panel"),
isOn: $alwaysExpandCandidateWindow
)
UserDef.kAlwaysExpandCandidateWindow.bind($alwaysExpandCandidateWindow).render()
.disabled(candidateWindowShowOnlyOneLine)
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey(UserDef.kRespectClientAccentColor.metaData?.shortTitle ?? "[i18n]respectClientAccentColor"),
isOn: $respectClientAccentColor
)
Text(
UserDef.kRespectClientAccentColor.metaData?.description?.localized ?? "[i18n]respectClientAccentColor.description"
)
.settingsDescription()
}
UserDef.kRespectClientAccentColor.bind($respectClientAccentColor).render()
}
// MARK: (header: Text("Misc Settings:"))
Section {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Show available reverse-lookup results in candidate window"),
isOn: $showReverseLookupInCandidateUI
)
Text(
"The lookup results are supplied by the CIN cassette module.".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Always use fixed listing order in candidate window"),
isOn: $useFixedCandidateOrderOnSelection
)
Text(
LocalizedStringKey(
"This will stop user override model from affecting how candidates get sorted."
)
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Consolidate the context on confirming candidate selection"),
isOn: $consolidateContextOnCandidateSelection
)
Text(
"For example: When typing “章太炎” and you want to override the “太” with “泰”, and the raw operation index range [1,2) which bounds are cutting the current node “章太炎” in range [0,3). If having lack of the pre-consolidation process, this word will become something like “張泰言” after the candidate selection. Only if we enable this consolidation, this word will become “章泰炎” which is the expected result that the context is kept as-is.".localized
)
.settingsDescription()
}
UserDef.kShowReverseLookupInCandidateUI.bind($showReverseLookupInCandidateUI).render()
UserDef.kUseFixedCandidateOrderOnSelection.bind($useFixedCandidateOrderOnSelection).render()
UserDef.kConsolidateContextOnCandidateSelection.bind($consolidateContextOnCandidateSelection).render()
}
// MARK: (header: Text("Experimental:"))
@ -196,10 +100,7 @@ public struct VwrSettingsPaneCandidates: View {
}
Section(footer: imkEOSNoticeButton) {
Toggle(
LocalizedStringKey("Enable mouse wheel support for Tadokoro Candidate Window"),
isOn: $enableMouseScrollingForTDKCandidatesCocoa
)
UserDef.kEnableMouseScrollingForTDKCandidatesCocoa.bind($enableMouseScrollingForTDKCandidatesCocoa).render()
}
}.formStyled()
}
@ -220,6 +121,7 @@ struct VwrSettingsPaneCandidates_Previews: PreviewProvider {
// MARK: - Selection Key Preferences (View)
@available(macOS 13, *)
/// View Struct
private struct VwrSettingsPaneCandidates_SelectionKeys: View {
// MARK: - AppStorage Variables
@ -229,12 +131,8 @@ private struct VwrSettingsPaneCandidates_SelectionKeys: View {
// MARK: - Main View
var body: some View {
HStack {
Text("Selection Keys:")
Spacer()
ComboBox(
items: CandidateKey.suggestions,
text: $candidateKeys.onChange {
UserDef.kCandidateKeys.bind(
$candidateKeys.onChange {
let value = candidateKeys
let keys = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased().deduplicated
// Start Error Handling.
@ -249,80 +147,6 @@ private struct VwrSettingsPaneCandidates_SelectionKeys: View {
}
}
}
).frame(width: 180)
}
Text(
"Choose or hit Enter to confim your prefered keys for selecting candidates.".localized
+ "\n"
+ "This will also affect the row / column capacity of the candidate window.".localized
)
.settingsDescription()
}
}
// MARK: - NSComboBox
// Ref: https://stackoverflow.com/a/71058587/4162914
// License: https://creativecommons.org/licenses/by-sa/4.0/
@available(macOS 10.15, *)
public struct ComboBox: NSViewRepresentable {
// The items that will show up in the pop-up menu:
public var items: [String] = []
// The property on our parent view that gets synced to the current
// stringValue of the NSComboBox, whether the user typed it in or
// selected it from the list:
@Binding public var text: String
public func makeCoordinator() -> Coordinator {
Coordinator(self)
}
public func makeNSView(context: Context) -> NSComboBox {
let comboBox = NSComboBox()
comboBox.usesDataSource = false
comboBox.completes = false
comboBox.delegate = context.coordinator
comboBox.intercellSpacing = NSSize(width: 0.0, height: 10.0)
return comboBox
}
public func updateNSView(_ nsView: NSComboBox, context: Context) {
nsView.removeAllItems()
nsView.addItems(withObjectValues: items)
// ComboBox doesn't automatically select the item matching its text;
// we must do that manually. But we need the delegate to ignore that
// selection-change or we'll get a "state modified during view update;
// will cause undefined behavior" warning.
context.coordinator.ignoreSelectionChanges = true
nsView.stringValue = text
nsView.selectItem(withObjectValue: text)
context.coordinator.ignoreSelectionChanges = false
}
public class Coordinator: NSObject, NSComboBoxDelegate {
public var parent: ComboBox
public var ignoreSelectionChanges = false
public init(_ parent: ComboBox) {
self.parent = parent
}
public func comboBoxSelectionDidChange(_ notification: Notification) {
if !ignoreSelectionChanges,
let box: NSComboBox = notification.object as? NSComboBox,
let newStringValue: String = box.objectValueOfSelectedItem as? String
{
parent.text = newStringValue
}
}
public func controlTextDidEndEditing(_ obj: Notification) {
if let textField = obj.object as? NSTextField {
parent.text = textField.stringValue
}
}
).render()
}
}

View File

@ -149,39 +149,9 @@ public struct VwrSettingsPaneCassette: View {
// MARK: - Something Else
Section {
Toggle(
LocalizedStringKey("Auto-composite when the longest possible key is formed"),
isOn: $autoCompositeWithLongestPossibleCassetteKey
)
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Show translated strokes in composition buffer"),
isOn: $showTranslatedStrokesInCompositionBuffer
)
Text(
LocalizedStringKey(
"All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file."
)
)
.settingsDescription()
}
VStack(alignment: .leading) {
Picker(
"Chinese Conversion:",
selection: $forceCassetteChineseConversion
) {
Text(LocalizedStringKey("Disable forced conversion for cassette outputs")).tag(0)
Text(LocalizedStringKey("Enforce conversion in both input modes")).tag(1)
Text(LocalizedStringKey("Only enforce conversion in Simplified Chinese mode")).tag(2)
Text(LocalizedStringKey("Only enforce conversion in Traditional Chinese mode")).tag(3)
}
Text(
LocalizedStringKey(
"This conversion only affects the cassette module, converting typed contents to either Simplified Chinese or Traditional Chinese in accordance with this setting and your current input mode."
)
)
.settingsDescription()
}
UserDef.kAutoCompositeWithLongestPossibleCassetteKey.bind($autoCompositeWithLongestPossibleCassetteKey).render()
UserDef.kShowTranslatedStrokesInCompositionBuffer.bind($showTranslatedStrokesInCompositionBuffer).render()
UserDef.kForceCassetteChineseConversion.bind($forceCassetteChineseConversion).render()
}
}.formStyled()
}

View File

@ -40,36 +40,11 @@ public struct VwrSettingsPaneDevZone: View {
Section(header: Text(
"Warning: This page is for testing future features. \nFeatures listed here may not work as expected."
), footer: Text("Some previous options are moved to other tabs.".localized).settingsDescription()) {
VStack(alignment: .leading) {
Toggle(
UserDef.kSecurityHardenedCompositionBuffer.metaData?.shortTitle?.localized ?? "",
isOn: $securityHardenedCompositionBuffer
)
Text(
UserDef.kSecurityHardenedCompositionBuffer.metaData?.description?.localized ?? ""
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
"Disable segmented thick underline in marking mode for managed clients".localized,
isOn: $disableSegmentedThickUnderlineInMarkingModeForManagedClients
)
Text(
"Some clients with web-based front UI may have issues rendering segmented thick underlines drawn by their implemented “setMarkedText()”. This option stops the input method from delivering segmented thick underlines to “client().setMarkedText()”. Note that segmented thick underlines are only used in marking mode, unless the client itself misimplements the IMKTextInput method “setMarkedText()”. This option only affects the inline composition buffer.".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
(UserDef.kCheckAbusersOfSecureEventInputAPI.metaData?.shortTitle ?? "i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.shortTitle").localized,
isOn: $checkAbusersOfSecureEventInputAPI
)
Text(
(UserDef.kCheckAbusersOfSecureEventInputAPI.metaData?.description ?? "i18n:UserDef.kCheckAbusersOfSecureEventInputAPI.description").localized
)
.settingsDescription()
}
UserDef.kSecurityHardenedCompositionBuffer.bind($securityHardenedCompositionBuffer).render()
UserDef.kDisableSegmentedThickUnderlineInMarkingModeForManagedClients
.bind($disableSegmentedThickUnderlineInMarkingModeForManagedClients)
.render()
UserDef.kCheckAbusersOfSecureEventInputAPI.bind($checkAbusersOfSecureEventInputAPI).render()
}
}.formStyled()
}

View File

@ -147,82 +147,45 @@ public struct VwrSettingsPaneDictionary: View {
)
)
.settingsDescription()
Toggle(
LocalizedStringKey("Automatically reload user data files if changes detected"),
isOn: $shouldAutoReloadUserDataFiles.onChange {
UserDef.kShouldAutoReloadUserDataFiles.bind(
$shouldAutoReloadUserDataFiles.onChange {
if shouldAutoReloadUserDataFiles {
LMMgr.initUserLangModels()
}
}
)
).render()
}
}
}
Section {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Read external factory dictionary files if possible"),
isOn: $useExternalFactoryDict.onChange {
UserDef.kUseExternalFactoryDict.bind(
$useExternalFactoryDict.onChange {
LMMgr.connectCoreDB()
}
)
Text(
LocalizedStringKey(
"This will use the SQLite database deployed by the “make install” command from libvChewing-Data if possible."
)
)
.settingsDescription()
}
Toggle(
LocalizedStringKey("Enable CNS11643 Support (2023-11-06)"),
isOn: $cns11643Enabled.onChange {
).render()
UserDef.kCNS11643Enabled.bind(
$cns11643Enabled.onChange {
LMMgr.syncLMPrefs()
}
)
Toggle(
LocalizedStringKey("Enable symbol input support (incl. certain emoji symbols)"),
isOn: $symbolInputEnabled.onChange {
).render()
UserDef.kSymbolInputEnabled.bind(
$symbolInputEnabled.onChange {
LMMgr.syncLMPrefs()
}
)
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Applying typing suggestions from half-life user override model"),
isOn: $fetchSuggestionsFromUserOverrideModel
)
Text(
"The user override model only possesses memories temporarily. Each memory record gradually becomes ineffective within approximately less than 6 days. You can erase all memory records through the input method menu.".localized
)
.settingsDescription()
}
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Enable phrase replacement table"),
isOn: $phraseReplacementEnabled.onChange {
).render()
UserDef.kFetchSuggestionsFromUserOverrideModel.bind($fetchSuggestionsFromUserOverrideModel).render()
UserDef.kPhraseReplacementEnabled.bind(
$phraseReplacementEnabled.onChange {
LMMgr.syncLMPrefs()
if phraseReplacementEnabled {
LMMgr.loadUserPhraseReplacement()
}
}
)
Text("This will batch-replace specified candidates.".localized)
.settingsDescription()
}
).render()
}
Section {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Allow boosting / excluding a candidate of single kanji when marking"),
isOn: $allowBoostingSingleKanjiAsUserPhrase
)
Text(
LocalizedStringKey(
"⚠︎ This may hinder the walking algorithm from giving appropriate results."
)
)
.settingsDescription()
}
UserDef.kAllowBoostingSingleKanjiAsUserPhrase.bind($allowBoostingSingleKanjiAsUserPhrase).render()
} footer: {
HStack {
Spacer()

View File

@ -82,67 +82,53 @@ public struct VwrSettingsPaneGeneral: View {
)
)
.settingsDescription()
Picker("UI Language:", selection: $appleLanguageTag) {
Text(LocalizedStringKey("Follow OS settings")).tag("auto")
Text(LocalizedStringKey("Simplified Chinese")).tag("zh-Hans")
Text(LocalizedStringKey("Traditional Chinese")).tag("zh-Hant")
Text(LocalizedStringKey("Japanese")).tag("ja")
Text(LocalizedStringKey("English")).tag("en")
}
Text(LocalizedStringKey("Change user interface language (will reboot the IME)."))
.settingsDescription()
UserDef.kAppleLanguages.bind($appleLanguageTag).render()
}
// MARK: (header: Text("Typing Settings:"))
Section {
VStack(alignment: .leading) {
let meta = UserDef.kReadingNarrationCoverage.metaData
let mainKey = "i18n:UserDef.kReadingNarrationCoverage"
Picker(
LocalizedStringKey(meta?.shortTitle ?? mainKey + ".shortTitle"),
selection: $readingNarrationCoverage.onChange {
UserDef.kReadingNarrationCoverage.bind(
$readingNarrationCoverage.onChange {
SpeechSputnik.shared.refreshStatus()
}
) {
Text(LocalizedStringKey(meta?.options?[0] ?? mainKey + ".option.nothing")).tag(0)
Text(LocalizedStringKey(meta?.options?[1] ?? mainKey + ".option.confirmed")).tag(1)
Text(LocalizedStringKey(meta?.options?[2] ?? mainKey + ".option.realtime")).tag(2)
}
Text(LocalizedStringKey(meta?.description ?? mainKey + ".description"))
.settingsDescription()
}
Toggle(
LocalizedStringKey("Automatically correct reading combinations when typing"),
isOn: $autoCorrectReadingCombination
)
Toggle(
LocalizedStringKey("Show Hanyu-Pinyin in the inline composition buffer"),
isOn: $showHanyuPinyinInCompositionBuffer
)
Toggle(
LocalizedStringKey("Allow backspace-editing miscomposed readings"),
isOn: $keepReadingUponCompositionError
)
Toggle(
LocalizedStringKey("Also use “\\” or “¥” key for Hanin Keyboard Symbol Input"),
isOn: $classicHaninKeyboardSymbolModeShortcutEnabled
)
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Emulating select-candidate-per-character mode"),
isOn: $useSCPCTypingMode.onChange {
).render()
UserDef.kAutoCorrectReadingCombination.bind($autoCorrectReadingCombination).render()
UserDef.kShowHanyuPinyinInCompositionBuffer.bind($showHanyuPinyinInCompositionBuffer).render()
UserDef.kKeepReadingUponCompositionError.bind($keepReadingUponCompositionError).render()
UserDef.kClassicHaninKeyboardSymbolModeShortcutEnabled
.bind($classicHaninKeyboardSymbolModeShortcutEnabled).render()
UserDef.kUseSCPCTypingMode.bind(
$useSCPCTypingMode.onChange {
guard useSCPCTypingMode else { return }
LMMgr.loadSCPCSequencesData()
}
)
Text(LocalizedStringKey("An accommodation for elder computer users."))
.settingsDescription()
}
).render()
if Date.isTodayTheDate(from: 0401) {
Toggle(
LocalizedStringKey("Stop farting (when typed phonetic combination is invalid, etc.)"),
isOn: $shouldNotFartInLieuOfBeep.onChange {
UserDef.kShouldNotFartInLieuOfBeep.bind(
$shouldNotFartInLieuOfBeep.onChange { onFartControlChange() }
).render()
}
}
// MARK: (header: Text("Misc Settings:"))
Section {
HStack {
UserDef.kCheckUpdateAutomatically.bind($checkUpdateAutomatically).render()
Divider()
UserDef.kIsDebugModeEnabled.bind($isDebugModeEnabled).render()
}
}
}.formStyled()
}
.frame(
minWidth: CtlSettingsUI.formWidth,
maxHeight: CtlSettingsUI.contentMaxHeight
)
}
private func onFartControlChange() {
let content = String(
format: NSLocalizedString(
"You are about to uncheck this fart suppressor. You are responsible for all consequences lead by letting people nearby hear the fart sound come from your computer. We strongly advise against unchecking this in any public circumstance that prohibits NSFW netas.",
@ -171,29 +157,6 @@ public struct VwrSettingsPaneGeneral: View {
}
IMEApp.buzz()
}
)
}
}
// MARK: (header: Text("Misc Settings:"))
Section {
Toggle(
LocalizedStringKey("Check for updates automatically"),
isOn: $checkUpdateAutomatically
)
Toggle(
LocalizedStringKey("Debug Mode"),
isOn: $isDebugModeEnabled
)
}
}.formStyled()
}
.frame(
minWidth: CtlSettingsUI.formWidth,
maxHeight: CtlSettingsUI.contentMaxHeight
)
}
}
@available(macOS 13, *)

View File

@ -58,67 +58,9 @@ public struct VwrSettingsPaneKeyboard: View {
Text("↻A")
}
}
VStack(alignment: .leading) {
Picker(
"Phonetic Parser:",
selection: $keyboardParser
) {
ForEach(KeyboardParser.allCases, id: \.self) { item in
if [7, 100].contains(item.rawValue) { Divider() }
Text(item.localizedMenuName).tag(item.rawValue)
}.id(UUID())
}
Spacer()
Text(NSLocalizedString("Choose the phonetic layout for Mandarin parser.", comment: ""))
.settingsDescription()
}
VStack(alignment: .leading) {
HStack {
Picker(
"Basic Keyboard Layout:",
selection: $basicKeyboardLayout
) {
ForEach(0 ... (IMKHelper.allowedBasicLayoutsAsTISInputSources.count - 1), id: \.self) { id in
let theEntry = IMKHelper.allowedBasicLayoutsAsTISInputSources[id]
if let theEntry = theEntry {
Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier)
} else {
Divider()
}
}.id(UUID())
}
}
Spacer()
Text(
NSLocalizedString(
"Choose the macOS-level basic keyboard layout. Non-QWERTY alphanumerical keyboard layouts are for Pinyin parser only. This option will only affect the appearance of the on-screen-keyboard if the current Mandarin parser is neither (any) pinyin nor dynamically reparsable with different western keyboard layouts (like Eten 26, Hsu, etc.).",
comment: ""
)
)
.settingsDescription()
}
VStack(alignment: .leading) {
HStack {
Picker(
"Alphanumerical Layout:",
selection: $alphanumericalKeyboardLayout
) {
ForEach(0 ... (IMKHelper.allowedAlphanumericalTISInputSources.count - 1), id: \.self) { id in
let theEntry = IMKHelper.allowedAlphanumericalTISInputSources[id]
Text(theEntry.vChewingLocalizedName).tag(theEntry.identifier)
}.id(UUID())
}
}
Spacer()
Text(
NSLocalizedString(
"Choose the macOS-level alphanumerical keyboard layout. This setting is for Shift-toggled alphanumerical mode only.",
comment: ""
)
)
.settingsDescription()
}
UserDef.kKeyboardParser.bind($keyboardParser).render()
UserDef.kBasicKeyboardLayout.bind($basicKeyboardLayout).render()
UserDef.kAlphanumericalKeyboardLayout.bind($alphanumericalKeyboardLayout).render()
}
Section(header: Text("Keyboard Shortcuts:")) {
VwrSettingsPaneKeyboard_KeyboardShortcuts()
@ -171,49 +113,19 @@ private struct VwrSettingsPaneKeyboard_KeyboardShortcuts: View {
var body: some View {
HStack(alignment: .top, spacing: NSFont.systemFontSize) {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Per-Char Select Mode"),
isOn: $usingHotKeySCPC
)
Toggle(
LocalizedStringKey("Associated Phrases"),
isOn: $usingHotKeyAssociates
)
Toggle(
LocalizedStringKey("CNS11643 Mode"),
isOn: $usingHotKeyCNS
)
Toggle(
LocalizedStringKey("Force KangXi Writing"),
isOn: $usingHotKeyKangXi
)
Toggle(
LocalizedStringKey("Reverse Lookup (Phonabets)"),
isOn: $usingHotKeyRevLookup
)
UserDef.kUsingHotKeySCPC.bind($usingHotKeySCPC).render()
UserDef.kUsingHotKeyAssociates.bind($usingHotKeyAssociates).render()
UserDef.kUsingHotKeyCNS.bind($usingHotKeyCNS).render()
UserDef.kUsingHotKeyKangXi.bind($usingHotKeyKangXi).render()
UserDef.kUsingHotKeyRevLookup.bind($usingHotKeyRevLookup).render()
}
Divider()
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("JIS Shinjitai Output"),
isOn: $usingHotKeyJIS
)
Toggle(
LocalizedStringKey("Half-Width Punctuation Mode"),
isOn: $usingHotKeyHalfWidthASCII
)
Toggle(
LocalizedStringKey("Currency Numeral Output"),
isOn: $usingHotKeyCurrencyNumerals
)
Toggle(
LocalizedStringKey("CIN Cassette Mode"),
isOn: $usingHotKeyCassette
)
Toggle(
LocalizedStringKey("CHS / CHT Input Mode Switch"),
isOn: $usingHotKeyInputMode
)
UserDef.kUsingHotKeyJIS.bind($usingHotKeyJIS).render()
UserDef.kUsingHotKeyHalfWidthASCII.bind($usingHotKeyHalfWidthASCII).render()
UserDef.kUsingHotKeyCurrencyNumerals.bind($usingHotKeyCurrencyNumerals).render()
UserDef.kUsingHotKeyCassette.bind($usingHotKeyCassette).render()
UserDef.kUsingHotKeyInputMode.bind($usingHotKeyInputMode).render()
}
}
}

View File

@ -35,42 +35,25 @@ public struct VwrSettingsPaneOutput: View {
ScrollView {
Form {
Section {
Toggle(
LocalizedStringKey("Auto-convert traditional Chinese glyphs to KangXi characters"),
isOn: $chineseConversionEnabled.onChange {
UserDef.kChineseConversionEnabled.bind(
$chineseConversionEnabled.onChange {
if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled {
shiftJISShinjitaiOutputEnabled = false
}
}
)
Toggle(
LocalizedStringKey("Auto-convert traditional Chinese glyphs to JIS Shinjitai characters"),
isOn: $shiftJISShinjitaiOutputEnabled.onChange {
).render()
UserDef.kShiftJISShinjitaiOutputEnabled.bind(
$shiftJISShinjitaiOutputEnabled.onChange {
if chineseConversionEnabled, shiftJISShinjitaiOutputEnabled {
chineseConversionEnabled = false
}
}
)
Toggle(
LocalizedStringKey("Commit Hanyu-Pinyin instead on Ctrl(+Option)+Command+Enter"),
isOn: $inlineDumpPinyinInLieuOfZhuyin
)
Toggle(
LocalizedStringKey("Trim unfinished readings / strokes on commit"),
isOn: $trimUnfinishedReadingsOnCommit
)
).render()
UserDef.kInlineDumpPinyinInLieuOfZhuyin.bind($inlineDumpPinyinInLieuOfZhuyin).render()
UserDef.kTrimUnfinishedReadingsOnCommit.bind($trimUnfinishedReadingsOnCommit).render()
}
Section(header: Text("Experimental:")) {
VStack(alignment: .leading) {
Toggle(
LocalizedStringKey("Harden vertical punctuations during vertical typing (not recommended)"),
isOn: $hardenVerticalPunctuations
)
Text(
"⚠︎ This feature is useful ONLY WHEN the font you are using doesn't support dynamic vertical punctuations. However, typed vertical punctuations will always shown as vertical punctuations EVEN IF your editor has changed the typing direction to horizontal."
)
.settingsDescription()
}
UserDef.kHardenVerticalPunctuations.bind($hardenVerticalPunctuations).render()
}
}.formStyled()
}