From 9faed2153fc4b5904188d0b592f25714fac167a9 Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 18:05:57 +0800 Subject: [PATCH] Uses property wrappers to manage preferences. --- McBopomofo.xcodeproj/project.pbxproj | 4 + Source/InputMethodController.h | 5 +- Source/InputMethodController.mm | 183 +++------------- Source/Preferences.swift | 260 +++++++++++++++++++++++ Source/PreferencesWindowController.swift | 27 +-- 5 files changed, 307 insertions(+), 172 deletions(-) create mode 100644 Source/Preferences.swift diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 4d65989e..50b2ef0e 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ D427F7B4279086DC004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B3279086DC004A2160 /* InputSourceHelper */; }; D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; }; D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7C027908EFC004A2160 /* OpenCCBridge */; }; + D44FB74527915565003C80A6 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44FB74427915555003C80A6 /* Preferences.swift */; }; D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */; }; D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */; }; D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */; }; @@ -175,6 +176,7 @@ D427F7AC27907B7E004A2160 /* NotifierUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NotifierUI; path = Packages/NotifierUI; sourceTree = ""; }; D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = ""; }; D427F7BF27908EAC004A2160 /* OpenCCBridge */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenCCBridge; path = Packages/OpenCCBridge; sourceTree = ""; }; + D44FB74427915555003C80A6 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonModalAlertWindowController.swift; sourceTree = ""; }; D47F7DD1278C1263002F9DD7 /* UserOverrideModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserOverrideModel.h; sourceTree = ""; }; @@ -251,6 +253,7 @@ D41355D7278D7409005E5CBD /* LanguageModelManager.mm */, 6A0D4EC815FC0D6400ABF4B3 /* main.m */, D427F76B278CA1BA004A2160 /* AppDelegate.swift */, + D44FB74427915555003C80A6 /* Preferences.swift */, D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */, 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, @@ -577,6 +580,7 @@ files = ( D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */, 6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */, + D44FB74527915565003C80A6 /* Preferences.swift in Sources */, D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */, 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */, diff --git a/Source/InputMethodController.h b/Source/InputMethodController.h index 10a18e5a..c1744637 100644 --- a/Source/InputMethodController.h +++ b/Source/InputMethodController.h @@ -72,9 +72,6 @@ // current input mode NSString *_inputMode; - - // if Chinese conversion is enabled - BOOL _chineseConversionEnabled; - BOOL _halfWidthPunctuationEnabled; } + @end diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 50140750..44fa5275 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -53,40 +53,7 @@ using namespace Formosa::Gramambular; using namespace McBopomofo; using namespace OpenVanilla; -// default, min and max candidate list text size -static const NSInteger kDefaultCandidateListTextSize = 16; static const NSInteger kMinKeyLabelSize = 10; -static const NSInteger kMinCandidateListTextSize = 12; -static const NSInteger kMaxCandidateListTextSize = 196; - -// default, min and max composing buffer size (in codepoints) -// modern Macs can usually work up to 16 codepoints when the builder still -// walks the grid with good performance; slower Macs (like old PowerBooks) -// will start to sputter beyond 12; such is the algorithmatic complexity -// of the Viterbi algorithm used in the builder library (at O(N^2)) -static const NSInteger kDefaultComposingBufferSize = 10; -static const NSInteger kMinComposingBufferSize = 4; -static const NSInteger kMaxComposingBufferSize = 20; - -// user defaults (app perferences) key names; in this project we use -// NSUserDefaults throughout and do not wrap them in another config object -static NSString *const kKeyboardLayoutPreferenceKey = @"KeyboardLayout"; -static NSString *const kBasisKeyboardLayoutPreferenceKey = @"BasisKeyboardLayout"; // alphanumeric ("ASCII") input basis -static NSString *const kFunctionKeyKeyboardLayoutPreferenceKey = @"FunctionKeyKeyboardLayout"; // alphanumeric ("ASCII") input basis -static NSString *const kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey = @"FunctionKeyKeyboardLayoutOverrideIncludeShift"; // whether include shift -static NSString *const kCandidateListTextSizeKey = @"CandidateListTextSize"; -static NSString *const kSelectPhraseAfterCursorAsCandidatePreferenceKey = @"SelectPhraseAfterCursorAsCandidate"; -static NSString *const kUseHorizontalCandidateListPreferenceKey = @"UseHorizontalCandidateList"; -static NSString *const kComposingBufferSizePreferenceKey = @"ComposingBufferSize"; -static NSString *const kChooseCandidateUsingSpaceKey = @"ChooseCandidateUsingSpaceKey"; -static NSString *const kChineseConversionEnabledKey = @"ChineseConversionEnabledKey"; -static NSString *const kHalfWidthPunctuationEnabledKey = @"HalfWidthPunctuationEnabledKey"; -static NSString *const kEscToCleanInputBufferKey = @"EscToCleanInputBufferKey"; - -// advanced (usually optional) settings -static NSString *const kCandidateTextFontName = @"CandidateTextFontName"; -static NSString *const kCandidateKeyLabelFontName = @"CandidateKeyLabelFontName"; -static NSString *const kCandidateKeys = @"CandidateKeys"; // input modes static NSString *const kBopomofoModeIdentifier = @"org.openvanilla.inputmethod.McBopomofo.Bopomofo"; @@ -130,12 +97,6 @@ static inline NSString *LocalizationNotNeeded(NSString *s) { return s; } -// private methods -@interface McBopomofoInputMethodController () -+ (VTHorizontalCandidateController *)horizontalCandidateController; -+ (VTVerticalCandidateController *)verticalCandidateController; -@end - @interface McBopomofoInputMethodController (VTCandidateController) @end @@ -201,8 +162,6 @@ static double FindHighestScore(const vector& nodes, double epsilon) _composingBuffer = [[NSMutableString alloc] init]; _inputMode = kBopomofoModeIdentifier; - _chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; - _halfWidthPunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfWidthPunctuationEnabledKey]; } return self; @@ -217,11 +176,11 @@ static double FindHighestScore(const vector& nodes, double epsilon) NSMenuItem *chineseConversionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Conversion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"g"]; chineseConversionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; - chineseConversionMenuItem.state = _chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff; + chineseConversionMenuItem.state = Preferences.chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff; [menu addItem:chineseConversionMenuItem]; NSMenuItem *halfWidthPunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Width Punctuations", @"") action:@selector(toggleHalfWidthPunctuation:) keyEquivalent:@""]; - halfWidthPunctuationMenuItem.state = _halfWidthPunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; + halfWidthPunctuationMenuItem.state = Preferences.halfWidthPunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; [menu addItem:halfWidthPunctuationMenuItem]; [menu addItem:[NSMenuItem separatorItem]]; @@ -258,10 +217,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) [[NSUserDefaults standardUserDefaults] synchronize]; // Override the keyboard layout. Use US if not set. - NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; - if (!basisKeyboardLayoutID) { - basisKeyboardLayoutID = @"com.apple.keylayout.US"; - } + NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout; [client overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID]; // reset the state @@ -272,7 +228,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) [_composingBuffer setString:@""]; // checks and populates the default settings - NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; + NSInteger keyboardLayout = Preferences.keyboardLayout; switch (keyboardLayout) { case 0: _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout()); @@ -294,27 +250,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) break; default: _bpmfReadingBuffer->setKeyboardLayout(BopomofoKeyboardLayout::StandardLayout()); - [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:kKeyboardLayoutPreferenceKey]; - } - - // set the size - NSInteger textSize = [[NSUserDefaults standardUserDefaults] integerForKey:kCandidateListTextSizeKey]; - NSInteger previousTextSize = textSize; - if (textSize == 0) { - textSize = kDefaultCandidateListTextSize; - } - else if (textSize < kMinCandidateListTextSize) { - textSize = kMinCandidateListTextSize; - } - else if (textSize > kMaxCandidateListTextSize) { - textSize = kMaxCandidateListTextSize; - } - - if (textSize != previousTextSize) { - [[NSUserDefaults standardUserDefaults] setInteger:textSize forKey:kCandidateListTextSizeKey]; - } - if (![[NSUserDefaults standardUserDefaults] objectForKey:kChooseCandidateUsingSpaceKey]) { - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kChooseCandidateUsingSpaceKey]; + Preferences.keyboardLayout = 0; } [(AppDelegate *)[NSApp delegate] checkForUpdate]; @@ -360,10 +296,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) [[NSUserDefaults standardUserDefaults] synchronize]; // Remember to override the keyboard layout again -- treat this as an activate eventy - NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; - if (!basisKeyboardLayoutID) { - basisKeyboardLayoutID = @"com.apple.keylayout.US"; - } + NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout; [sender overrideKeyboardWithKeyboardNamed:basisKeyboardLayoutID]; _inputMode = newInputMode; @@ -402,8 +335,8 @@ static double FindHighestScore(const vector& nodes, double epsilon) // Chinese conversion. NSString *buffer = _composingBuffer; - BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; - if (chineseConversionEnabled) { + + if (Preferences.chineseConversionEnabled) { buffer = [OpenCCBridge convert:_composingBuffer]; } @@ -544,29 +477,14 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // the user type along, the already composed text at front will // be popped out - NSInteger _composingBufferSize = [[NSUserDefaults standardUserDefaults] integerForKey:kComposingBufferSizePreferenceKey]; - NSInteger previousComposingBufferSize = _composingBufferSize; + NSInteger composingBufferSize = Preferences.composingBufferSize; - if (_composingBufferSize == 0) { - _composingBufferSize = kDefaultComposingBufferSize; - } - else if (_composingBufferSize < kMinComposingBufferSize) { - _composingBufferSize = kMinComposingBufferSize; - } - else if (_composingBufferSize > kMaxComposingBufferSize) { - _composingBufferSize = kMaxComposingBufferSize; - } - - if (_composingBufferSize != previousComposingBufferSize) { - [[NSUserDefaults standardUserDefaults] setInteger:_composingBufferSize forKey:kComposingBufferSizePreferenceKey]; - } - - if (_builder->grid().width() > (size_t)_composingBufferSize) { + if (_builder->grid().width() > (size_t)composingBufferSize) { if (_walkedNodes.size() > 0) { NodeAnchor &anchor = _walkedNodes[0]; NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()]; // Chinese conversion. - BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; + BOOL chineseConversionEnabled = Preferences.chineseConversionEnabled; if (chineseConversionEnabled) { popedText = [OpenCCBridge convert:popedText]; } @@ -586,30 +504,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (string)_currentLayout { - string layout = string("Standard_");; - NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; - switch (keyboardLayout) { - case 0: - layout = string("Standard_"); - break; - case 1: - layout = string("ETen_"); - break; - case 2: - layout = string("ETen26_"); - break; - case 3: - layout = string("Hsu_"); - break; - case 4: - layout = string("HanyuPinyin_"); - break; - case 5: - layout = string("IBM_"); - break; - default: - break; - } + NSString *keyboardLayoutName = Preferences.keyboardLayoutName; + string layout = string(keyboardLayoutName.UTF8String) + string("_"); return layout; } @@ -639,7 +535,6 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return McBpomofoEmacsKeyNone; } - - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client { NSRect textFrame = NSZeroRect; @@ -835,7 +730,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } if (_bpmfReadingBuffer->isEmpty() && [_composingBuffer length] > 0 && (keyCode == extraChooseCandidateKey || charCode == 32 || (useVerticalMode && (keyCode == verticalModeOnlyChooseCandidateKey)))) { if (charCode == 32) { // if the spacebar is NOT set to be a selection key - if (![[NSUserDefaults standardUserDefaults] boolForKey:kChooseCandidateUsingSpaceKey]) { + if (!Preferences.chooseCandidateUsingSpace) { if (_builder->cursorIndex() >= _builder->length()) { [_composingBuffer appendString:@" "]; [self commitComposition:client]; @@ -856,7 +751,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // Esc if (charCode == 27) { - BOOL escToClearInputBufferEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kEscToCleanInputBufferKey]; + BOOL escToClearInputBufferEnabled = Preferences.escToCleanInputBuffer; if (escToClearInputBufferEnabled) { // if the optioon is enabled, we clear everythiong including the composing @@ -1079,7 +974,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // if nothing is matched, see if it's a punctuation key for current layout. string layout = [self _currentLayout]; - string punctuationNamePrefix = (_halfWidthPunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); + string punctuationNamePrefix = Preferences.halfWidthPunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_"); string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { return YES; @@ -1328,15 +1223,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (BOOL)handleEvent:(NSEvent *)event client:(id)client { if ([event type] == NSFlagsChanged) { - NSString *functionKeyKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kFunctionKeyKeyboardLayoutPreferenceKey]; - if (!functionKeyKeyboardLayoutID) { - functionKeyKeyboardLayoutID = @"com.apple.keylayout.US"; - } - - NSString *basisKeyboardLayoutID = [[NSUserDefaults standardUserDefaults] stringForKey:kBasisKeyboardLayoutPreferenceKey]; - if (!basisKeyboardLayoutID) { - basisKeyboardLayoutID = @"com.apple.keylayout.US"; - } + NSString *functionKeyKeyboardLayoutID = Preferences.functionKeyboardLayout; + NSString *basisKeyboardLayoutID = Preferences.basisKeyboardLayout; // If no override is needed, just return NO. if ([functionKeyKeyboardLayoutID isEqualToString:basisKeyboardLayoutID]) { @@ -1344,7 +1232,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // Function key pressed. - BOOL includeShift = [[NSUserDefaults standardUserDefaults] boolForKey:kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey]; + BOOL includeShift = Preferences.functionKeyKeyboardLayoutOverrideIncludeShiftKey; if (([event modifierFlags] & ~NSShiftKeyMask) || (([event modifierFlags] & NSShiftKeyMask) && includeShift)) { // Override the keyboard layout and let the OS do its thing [client overrideKeyboardWithKeyboardNamed:functionKeyKeyboardLayoutID]; @@ -1417,10 +1305,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (size_t)actualCandidateCursorIndex { size_t cursorIndex = _builder->cursorIndex(); - - BOOL candidatePhraseLocatedAfterCursor = [[NSUserDefaults standardUserDefaults] boolForKey:kSelectPhraseAfterCursorAsCandidatePreferenceKey]; - - if (candidatePhraseLocatedAfterCursor) { + if (Preferences.selectPhraseAfterCursorAsCandidate) { // MS Phonetics IME style, phrase is *after* the cursor, i.e. cursor is always *before* the phrase if (cursorIndex < _builder->length()) { ++cursorIndex; @@ -1438,12 +1323,11 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (void)_showCandidateWindowUsingVerticalMode:(BOOL)useVerticalMode client:(id)client { // set the candidate panel style - BOOL useHorizontalCandidateList = [[NSUserDefaults standardUserDefaults] boolForKey:kUseHorizontalCandidateListPreferenceKey]; if (useVerticalMode) { gCurrentCandidateController = [McBopomofoInputMethodController verticalCandidateController]; } - else if (useHorizontalCandidateList) { + else if (Preferences.useHorizontalCandidateList) { gCurrentCandidateController = [McBopomofoInputMethodController horizontalCandidateController]; } else { @@ -1451,16 +1335,16 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // set the attributes for the candidate panel (which uses NSAttributedString) - NSInteger textSize = [[NSUserDefaults standardUserDefaults] integerForKey:kCandidateListTextSizeKey]; + NSInteger textSize = Preferences.candidateListTextSize; NSInteger keyLabelSize = textSize / 2; if (keyLabelSize < kMinKeyLabelSize) { keyLabelSize = kMinKeyLabelSize; } - NSString *ctFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateTextFontName]; - NSString *klFontName = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeyLabelFontName]; - NSString *ckeys = [[NSUserDefaults standardUserDefaults] stringForKey:kCandidateKeys]; + NSString *ctFontName = Preferences.candidateTextFontName; + NSString *klFontName = Preferences.candidateKeyLabelFontName; + NSString *ckeys = Preferences.candidateKeys; gCurrentCandidateController.keyLabelFont = klFontName ? [NSFont fontWithName:klFontName size:keyLabelSize] : [NSFont systemFontOfSize:keyLabelSize]; gCurrentCandidateController.candidateFont = ctFontName ? [NSFont fontWithName:ctFontName size:textSize] : [NSFont systemFontOfSize:textSize]; @@ -1661,25 +1545,21 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (void)openUserPhrases:(id)sender { - NSLog(@"openUserPhrases called"); [self _openUserFile:[LanguageModelManager userPhrasesDataPathMcBopomofo]]; } - (void)openExcludedPhrasesPlainBopomofo:(id)sender { - NSLog(@"openExcludedPhrasesPlainBopomofo called"); [self _openUserFile:[LanguageModelManager excludedPhrasesDataPathPlainBopomofo]]; } - (void)openExcludedPhrasesMcBopomofo:(id)sender { - NSLog(@"openExcludedPhrasesMcBopomofo called"); [self _openUserFile:[LanguageModelManager excludedPhrasesDataPathMcBopomofo]]; } - (void)reloadUserPhrases:(id)sender { - NSLog(@"reloadUserPhrases called"); [LanguageModelManager loadUserPhrasesModel]; } @@ -1691,22 +1571,21 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } - (void)toggleChineseConverter:(id)sender { - _chineseConversionEnabled = !_chineseConversionEnabled; - [[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey]; - + BOOL chineseConversionEnabled = [Preferences toggleChineseConversionEnabled]; [NotifierController notifyWithMessage: - _chineseConversionEnabled ? + chineseConversionEnabled ? NSLocalizedString(@"Chinese conversion on", @"") : NSLocalizedString(@"Chinese conversion off", @"") stay:NO]; } - (void)toggleHalfWidthPunctuation:(id)sender { - _halfWidthPunctuationEnabled = !_halfWidthPunctuationEnabled; - [[NSUserDefaults standardUserDefaults] setBool:_halfWidthPunctuationEnabled forKey:kHalfWidthPunctuationEnabledKey]; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" + [Preferences toogleHalfWidthPunctuationEnabled]; +#pragma GCC diagnostic pop } - @end #pragma mark - diff --git a/Source/Preferences.swift b/Source/Preferences.swift new file mode 100644 index 00000000..eab3d635 --- /dev/null +++ b/Source/Preferences.swift @@ -0,0 +1,260 @@ +// +// Preferences.swift +// +// Copyright (c) 2011 The McBopomofo Project. +// +// Contributors: +// Mengjuei Hsieh (@mjhsieh) +// Weizhong Yang (@zonble) +// +// Based on the Syrup Project and the Formosana Library +// by Lukhnos Liu (@lukhnos). +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +import Cocoa + +private let kKeyboardLayoutPreferenceKey = "KeyboardLayout" +private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout"; // alphanumeric ("ASCII") input basi +private let kFunctionKeyKeyboardLayoutPreferenceKey = "FunctionKeyKeyboardLayout"; // alphanumeric ("ASCII") input basi +private let kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey = "FunctionKeyKeyboardLayoutOverrideIncludeShift"; // whether include shif +private let kCandidateListTextSizeKey = "CandidateListTextSize" +private let kSelectPhraseAfterCursorAsCandidatePreferenceKey = "SelectPhraseAfterCursorAsCandidate" +private let kUseHorizontalCandidateListPreferenceKey = "UseHorizontalCandidateList" +private let kComposingBufferSizePreferenceKey = "ComposingBufferSize" +private let kChooseCandidateUsingSpaceKey = "ChooseCandidateUsingSpaceKey" +private let kChineseConversionEnabledKey = "ChineseConversionEnabled" +private let kHalfWidthPunctuationEnabledKey = "HalfWidthPunctuationEnable" +private let kEscToCleanInputBufferKey = "EscToCleanInputBuffer" + +private let kCandidateTextFontName = "CandidateTextFontName" +private let kCandidateKeyLabelFontName = "CandidateKeyLabelFontName" +private let kCandidateKeys = "CandidateKeys" + +private let kDefaultCandidateListTextSize: CGFloat = 16 +private let kMinKeyLabelSize: CGFloat = 10 +private let kMinCandidateListTextSize: CGFloat = 12 +private let kMaxCandidateListTextSize: CGFloat = 196 + +// default, min and max composing buffer size (in codepoints) +// modern Macs can usually work up to 16 codepoints when the builder still +// walks the grid with good performance; slower Macs (like old PowerBooks) +// will start to sputter beyond 12; such is the algorithmatic complexity +// of the Viterbi algorithm used in the builder library (at O(N^2)) +private let kDefaultComposingBufferSize = 10 +private let kMinComposingBufferSize = 4 +private let kMaxComposingBufferSize = 20 + +private let kDefaultKeys = "123456789" + +// MARK: Property wrappers + +@propertyWrapper +struct UserDefault { + let key: String + let defaultValue: Value + var container: UserDefaults = .standard + + var wrappedValue: Value { + get { + return container.object(forKey: key) as? Value ?? defaultValue + } + set { + container.set(newValue, forKey: key) + } + } +} + +@propertyWrapper +struct CandidateListTextSize { + let key: String + let defaultValue: CGFloat = kDefaultCandidateListTextSize + lazy var container: UserDefault = { + UserDefault(key: key, defaultValue: defaultValue) }() + + var wrappedValue: CGFloat { + mutating get { + var value = container.wrappedValue + if value < kMinCandidateListTextSize { + value = kMinCandidateListTextSize + } else if value > kMaxCandidateListTextSize { + value = kMaxCandidateListTextSize + } + return value + } + set { + var value = newValue + if value < kMinCandidateListTextSize { + value = kMinCandidateListTextSize + } else if value > kMaxCandidateListTextSize { + value = kMaxCandidateListTextSize + } + container.wrappedValue = value + } + } +} + +@propertyWrapper +struct ComposingBufferSize { + let key: String + let defaultValue: Int = kDefaultComposingBufferSize + lazy var container: UserDefault = { + UserDefault(key: key, defaultValue: defaultValue) }() + + var wrappedValue: Int { + mutating get { + let currentValue = container.wrappedValue + if currentValue < kMinComposingBufferSize { + return kMinComposingBufferSize + } else if currentValue > kMaxComposingBufferSize { + return kMaxComposingBufferSize + } + return currentValue + } + set { + var value = newValue + if value < kMinComposingBufferSize { + value = kMinComposingBufferSize + } else if value > kMaxComposingBufferSize { + value = kMaxComposingBufferSize + } + container.wrappedValue = value + } + } +} + +@propertyWrapper +struct ComposingKeys { + let key: String + let defaultValue: String? = kCandidateKeys + lazy var container: UserDefault = { + UserDefault(key: key, defaultValue: defaultValue) }() + + var wrappedValue: String? { + mutating get { + let value = container.wrappedValue + if let value = value { + if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + return nil + } + } + return value + } + set { + let value = newValue + if let value = value { + if value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { + container.wrappedValue = nil + return + } + } + container.wrappedValue = value + } + } +} + + +// MARK: - + +class Preferences: NSObject { + @UserDefault(key: kKeyboardLayoutPreferenceKey, defaultValue: 0) + @objc static var keyboardLayout: Int + + @objc static var keyboardLayoutName: String { + switch self.keyboardLayout { + case 0: + return "Standard" + case 1: + return "ETen" + case 2: + return "ETen26" + case 3: + return "Hsu" + case 4: + return "HanyuPinyin" + case 5: + return "IBM" + default: + return "Standard" + } + } + + @UserDefault(key: kBasisKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US") + @objc static var basisKeyboardLayout: String + + @UserDefault(key: kFunctionKeyKeyboardLayoutPreferenceKey, defaultValue: "com.apple.keylayout.US") + @objc static var functionKeyboardLayout: String + + @UserDefault(key: kFunctionKeyKeyboardLayoutOverrideIncludeShiftKey, defaultValue: false) + @objc static var functionKeyKeyboardLayoutOverrideIncludeShiftKey: Bool + + @CandidateListTextSize(key: kCandidateListTextSizeKey) + @objc static var candidateListTextSize: CGFloat + + @UserDefault(key: kSelectPhraseAfterCursorAsCandidatePreferenceKey, defaultValue: false) + @objc static var selectPhraseAfterCursorAsCandidate: Bool + + @UserDefault(key: kUseHorizontalCandidateListPreferenceKey, defaultValue: false) + @objc static var useHorizontalCandidateList: Bool + + @ComposingBufferSize(key: kComposingBufferSizePreferenceKey) + @objc static var composingBufferSize: Int + + @UserDefault(key: kChooseCandidateUsingSpaceKey, defaultValue: true) + @objc static var chooseCandidateUsingSpace: Bool + + @UserDefault(key: kChineseConversionEnabledKey, defaultValue: true) + @objc static var chineseConversionEnabled: Bool + + @objc static func toggleChineseConversionEnabled() -> Bool { + chineseConversionEnabled = !chineseConversionEnabled + return chineseConversionEnabled + } + + @UserDefault(key: kHalfWidthPunctuationEnabledKey, defaultValue: true) + @objc static var halfWidthPunctuationEnabled: Bool + + @objc static func toogleHalfWidthPunctuationEnabled() -> Bool { + halfWidthPunctuationEnabled = !halfWidthPunctuationEnabled + return halfWidthPunctuationEnabled; + } + + @UserDefault(key: kEscToCleanInputBufferKey, defaultValue: true) + @objc static var escToCleanInputBuffer: Bool + + // MARK: Optional settings + + @UserDefault(key: kCandidateTextFontName, defaultValue: nil) + @objc static var candidateTextFontName: String? + + @UserDefault(key: kCandidateKeyLabelFontName, defaultValue: nil) + @objc static var candidateKeyLabelFontName: String? + + @ComposingKeys(key: kCandidateKeys) + @objc static var candidateKeys: String? + + @objc static var defaultKeys: String { + kDefaultKeys + } + +} diff --git a/Source/PreferencesWindowController.swift b/Source/PreferencesWindowController.swift index 0282d02b..1e93dab2 100644 --- a/Source/PreferencesWindowController.swift +++ b/Source/PreferencesWindowController.swift @@ -35,10 +35,6 @@ import Cocoa import Carbon -private let kBasisKeyboardLayoutPreferenceKey = "BasisKeyboardLayout" -private let kCandidateKeys = "CandidateKeys" -private let kDefaultKeys = "123456789" - // Please note that the class should be exposed as "PreferencesWindowController" // in Objective-C in order to let IMK to see the same class name as // the "InputMethodServerPreferencesWindowControllerClass" in Info.plist. @@ -54,7 +50,7 @@ private let kDefaultKeys = "123456789" basisKeyboardLayoutButton.menu?.removeAllItems() - let basisKeyboardLayoutID = UserDefaults.standard.string(forKey: kBasisKeyboardLayoutPreferenceKey) + let basisKeyboardLayoutID = Preferences.basisKeyboardLayout for source in list { if let categoryPtr = TISGetInputSourceProperty(source, kTISPropertyInputSourceCategory) { let category = Unmanaged.fromOpaque(categoryPtr).takeUnretainedValue() @@ -106,37 +102,36 @@ private let kDefaultKeys = "123456789" basisKeyboardLayoutButton.select(chosenItem ?? usKeyboardLayoutItem) selectionKeyComboBox.usesDataSource = false - selectionKeyComboBox.addItems(withObjectValues: [kDefaultKeys, "asdfghjkl", "asdfzxcvb"]) + selectionKeyComboBox.addItems(withObjectValues: [Preferences.defaultKeys, "asdfghjkl", "asdfzxcvb"]) - var candidateSelectionKeys = (UserDefaults.standard.string(forKey: kCandidateKeys) ?? kDefaultKeys) - .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + var candidateSelectionKeys = Preferences.candidateKeys ?? Preferences.defaultKeys if candidateSelectionKeys.isEmpty { - candidateSelectionKeys = kDefaultKeys + candidateSelectionKeys = Preferences.defaultKeys } selectionKeyComboBox.stringValue = candidateSelectionKeys } @IBAction func updateBasisKeyboardLayoutAction(_ sender: Any) { - if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject { - UserDefaults.standard.set(sourceID, forKey: kBasisKeyboardLayoutPreferenceKey) + if let sourceID = basisKeyboardLayoutButton.selectedItem?.representedObject as? String { + Preferences.basisKeyboardLayout = sourceID } } @IBAction func changeSelectionKeyAction(_ sender: Any) { let keys = (sender as AnyObject).stringValue.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) if keys.count != 9 || !keys.canBeConverted(to: .ascii) { - selectionKeyComboBox.stringValue = kDefaultKeys - UserDefaults.standard.removeObject(forKey: kCandidateKeys) + selectionKeyComboBox.stringValue = Preferences.defaultKeys + Preferences.candidateKeys = nil NSSound.beep() return } selectionKeyComboBox.stringValue = keys - if keys == kDefaultKeys { - UserDefaults.standard.removeObject(forKey: kCandidateKeys) + if keys == Preferences.defaultKeys { + Preferences.candidateKeys = nil } else { - UserDefaults.standard.set(keys, forKey: kCandidateKeys) + Preferences.candidateKeys = keys } }