From 232a944953589fc974b2cd500a4ebacfc4b943e4 Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 17:07:22 +0800 Subject: [PATCH 01/11] Implements half-size punctuations. --- Source/InputMethodController.h | 1 + Source/InputMethodController.mm | 26 +++++++++++++++++++----- Source/en.lproj/Localizable.strings | 2 ++ Source/zh-Hant.lproj/Localizable.strings | 2 ++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/Source/InputMethodController.h b/Source/InputMethodController.h index 404c0ab4..f1dbaea1 100644 --- a/Source/InputMethodController.h +++ b/Source/InputMethodController.h @@ -75,5 +75,6 @@ // if Chinese conversion is enabled BOOL _chineseConversionEnabled; + BOOL _halfSizePunctuationEnabled; } @end diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index a7fe79a0..26901fab 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -78,6 +78,7 @@ static NSString *const kUseHorizontalCandidateListPreferenceKey = @"UseHorizonta static NSString *const kComposingBufferSizePreferenceKey = @"ComposingBufferSize"; static NSString *const kChooseCandidateUsingSpaceKey = @"ChooseCandidateUsingSpaceKey"; static NSString *const kChineseConversionEnabledKey = @"ChineseConversionEnabledKey"; +static NSString *const kHalfSizePunctuationEnabledKey = @"HalfSizePunctuationEnabledKey"; static NSString *const kEscToCleanInputBufferKey = @"EscToCleanInputBufferKey"; // advanced (usually optional) settings @@ -189,6 +190,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) _inputMode = kBopomofoModeIdentifier; _chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; + _halfSizePunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfSizePunctuationEnabledKey]; } return self; @@ -201,11 +203,15 @@ static double FindHighestScore(const vector& nodes, double epsilon) NSMenuItem *preferenceMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"McBopomofo Preferences", @"") action:@selector(showPreferences:) keyEquivalent:@""]; [menu addItem:preferenceMenuItem]; - NSMenuItem *chineseConversionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Conversion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"G"]; + NSMenuItem *chineseConversionMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Chinese Conversion", @"") action:@selector(toggleChineseConverter:) keyEquivalent:@"g"]; chineseConversionMenuItem.keyEquivalentModifierMask = NSEventModifierFlagCommand | NSEventModifierFlagControl; chineseConversionMenuItem.state = _chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff; [menu addItem:chineseConversionMenuItem]; + NSMenuItem *halfSizePunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Size Punctuations", @"") action:@selector(toggleHalfSizePunctuation:) keyEquivalent:@""]; + halfSizePunctuationMenuItem.state = _halfSizePunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; + [menu addItem:halfSizePunctuationMenuItem]; + [menu addItem:[NSMenuItem separatorItem]]; [menu addItemWithTitle:NSLocalizedString(@"User Phrases", @"") action:NULL keyEquivalent:@""]; @@ -382,7 +388,8 @@ static double FindHighestScore(const vector& nodes, double epsilon) // Chinese conversion. NSString *buffer = _composingBuffer; - if (_chineseConversionEnabled) { + BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; + if (chineseConversionEnabled) { buffer = [OpenCCBridge convert:_composingBuffer]; } @@ -542,7 +549,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } NodeAnchor &anchor = _walkedNodes[0]; NSString *popedText = [NSString stringWithUTF8String:anchor.node->currentKeyValue().value.c_str()]; // Chinese conversion. - if (_chineseConversionEnabled) { + BOOL chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; + if (chineseConversionEnabled) { popedText = [OpenCCBridge convert:popedText]; } [client insertText:popedText replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; @@ -1062,13 +1070,14 @@ 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 customPunctuation = string("_punctuation_") + layout + string(1, (char)charCode); + string punctuationNamePrefix = (_halfSizePunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); + string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); if ([self handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } // if nothing is matched, see if it's a punctuation key. - string punctuation = string("_punctuation_") + string(1, (char)charCode); + string punctuation = punctuationNamePrefix + string(1, (char)charCode); if ([self handlePunctuation:punctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } @@ -1548,6 +1557,13 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey]; } +- (void)toggleHalfSizePunctuation:(id)sender +{ + _halfSizePunctuationEnabled = !_halfSizePunctuationEnabled; + [[NSUserDefaults standardUserDefaults] setBool:_halfSizePunctuationEnabled forKey:kHalfSizePunctuationEnabledKey]; +} + + @end #pragma mark - diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index a9b5048e..7cfe431d 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -62,3 +62,5 @@ "Please check the permission of at \"%@\"." = "Please check the permission of at \"%@\"."; "Edit Excluded Phrases" = "Edit Excluded Phrases"; + +"Use Half-Size Punctuations" = "Use Half-Size Punctuations"; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index cdd2b6d2..015c4538 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -63,3 +63,5 @@ "Edit Excluded Phrases" = "編輯要排除的詞彙"; +"Use Half-Size Punctuations" = "使用半型標點符號"; + From 9cd2306313db888b929c71d77175255e2a01d12e Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 19:57:08 +0800 Subject: [PATCH 02/11] Adds emacs-style hotkeys. --- Source/InputMethodController.mm | 98 ++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 26901fab..a501406e 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -104,6 +104,16 @@ enum { kDeleteKeyCode = 117 }; +typedef NS_ENUM(NSUInteger, McBpomofoEmacsKey) { + McBpomofoEmacsKeyNone, + McBpomofoEmacsKeyForward, + McBpomofoEmacsKeyBackward, + McBpomofoEmacsKeyHome, + McBpomofoEmacsKeyEnd, + McBpomofoEmacsKeyDelete, + McBpomofoEmacsKeyNextPage, +}; + VTCandidateController *gCurrentCandidateController = nil; // if DEBUG is defined, a DOT file (GraphViz format) will be written to the @@ -567,7 +577,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } NSBeep(); } -- (string)currentLayout +- (string)_currentLayout { string layout = string("Standard_");; NSInteger keyboardLayout = [[NSUserDefaults standardUserDefaults] integerForKey:kKeyboardLayoutPreferenceKey]; @@ -636,6 +646,33 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return [LanguageModelManager writeUserPhrase:currentMarkedPhrase]; } +- (McBpomofoEmacsKey)detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags +{ + if (flags & NSControlKeyMask) { + char c = charCode + 'a' - 1; + if (c == 'a') { + return McBpomofoEmacsKeyHome; + } + else if (c == 'e') { + return McBpomofoEmacsKeyEnd; + } + else if (c == 'f') { + return McBpomofoEmacsKeyForward; + } + else if (c == 'b') { + return McBpomofoEmacsKeyBackward; + } + else if (c == 'd') { + return McBpomofoEmacsKeyDelete; + } + else if (c == 'v') { + return McBpomofoEmacsKeyNextPage; + } + } + return McBpomofoEmacsKeyNone; +} + + - (BOOL)handleInputText:(NSString*)inputText key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)client { NSRect textFrame = NSZeroRect; @@ -661,6 +698,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // get the unicode character code UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0; + McBpomofoEmacsKey emacsKey = [self detectEmacsKeyFromCharCode:charCode modifiers:flags]; + if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) { // special handling for com.apple.Terminal _currentDeferredClient = client; @@ -672,7 +711,9 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // if the composing buffer is empty and there's no reading, and there is some function key combination, we ignore it - if (![_composingBuffer length] && _bpmfReadingBuffer->isEmpty() && ((flags & NSCommandKeyMask) || (flags & NSControlKeyMask) || (flags & NSAlternateKeyMask) || (flags & NSNumericPadKeyMask))) { + if (![_composingBuffer length] && + _bpmfReadingBuffer->isEmpty() && + ((flags & NSCommandKeyMask) || (flags & NSControlKeyMask) || (flags & NSAlternateKeyMask) || (flags & NSNumericPadKeyMask))) { return NO; } @@ -716,7 +757,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // if we have candidate, it means we need to pass the event to the candidate handler if ([_candidates count]) { - return [self handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode]; + return [self _handleCandidateEventWithInputText:inputText charCode:charCode keyCode:keyCode emacsKey:(McBpomofoEmacsKey)emacsKey]; } // If we have marker index. @@ -739,7 +780,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + left - if (keyCode == cursorBackwardKey && (flags & NSShiftKeyMask)) { + if ((keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) + && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() > 0) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() - 1); } @@ -750,7 +792,8 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } // Shift + Right - if (keyCode == cursorForwardKey && (flags & NSShiftKeyMask)) { + if ((keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) + && (flags & NSShiftKeyMask)) { if (_builder->markerCursorIndex() < _builder->length()) { _builder->setMarkerCursorIndex(_builder->markerCursorIndex() + 1); } @@ -883,7 +926,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor backward - if (keyCode == cursorBackwardKey) { + if (keyCode == cursorBackwardKey || emacsKey == McBpomofoEmacsKeyBackward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -915,7 +958,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // handle cursor forward - if (keyCode == cursorForwardKey) { + if (keyCode == cursorForwardKey || emacsKey == McBpomofoEmacsKeyForward) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -945,7 +988,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kHomeKeyCode) { + if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -966,7 +1009,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - if (keyCode == kEndKeyCode) { + if (keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) { if (!_bpmfReadingBuffer->isEmpty()) { [self beep]; } @@ -1019,7 +1062,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } // Delete - if (keyCode == kDeleteKeyCode) { + if (keyCode == kDeleteKeyCode || emacsKey == McBpomofoEmacsKeyDelete) { if (_bpmfReadingBuffer->isEmpty()) { if (![_composingBuffer length]) { return NO; @@ -1041,7 +1084,6 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } - // Enter if (charCode == 13) { if (![_composingBuffer length]) { @@ -1069,16 +1111,16 @@ 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 layout = [self _currentLayout]; string punctuationNamePrefix = (_halfSizePunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); - if ([self handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { + if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } // if nothing is matched, see if it's a punctuation key. string punctuation = punctuationNamePrefix + string(1, (char)charCode); - if ([self handlePunctuation:punctuation usingVerticalMode:useVerticalMode client:client]) { + if ([self _handlePunctuation:punctuation usingVerticalMode:useVerticalMode client:client]) { return YES; } @@ -1094,7 +1136,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return NO; } -- (BOOL)handlePunctuation:(string)customPunctuation usingVerticalMode:(BOOL)useVerticalMode client:(id)client +- (BOOL)_handlePunctuation:(string)customPunctuation usingVerticalMode:(BOOL)useVerticalMode client:(id)client { if (_languageModel->hasUnigramsForKey(customPunctuation)) { if (_bpmfReadingBuffer->isEmpty()) { @@ -1120,7 +1162,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return NO; } -- (BOOL)handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode +- (BOOL)_handleCandidateEventWithInputText:(NSString *)inputText charCode:(UniChar)charCode keyCode:(NSUInteger)keyCode emacsKey:(McBpomofoEmacsKey)emacsKey { BOOL cancelCandidateKey = (charCode == 27) || @@ -1143,7 +1185,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self candidateController:gCurrentCandidateController didSelectCandidateAtIndex:gCurrentCandidateController.selectedCandidateIndex]; return YES; } - else if (charCode == 32 || keyCode == kPageDownKeyCode) { + else if (charCode == 32 || keyCode == kPageDownKeyCode || emacsKey == McBpomofoEmacsKeyNextPage) { BOOL updated = [gCurrentCandidateController showNextPage]; if (!updated) { [self beep]; @@ -1177,6 +1219,14 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } + else if (emacsKey == McBpomofoEmacsKeyBackward) { + BOOL updated = [gCurrentCandidateController highlightPreviousCandidate]; + if (!updated) { + [self beep]; + } + [self updateClientComposingBuffer:_currentCandidateClient]; + return YES; + } else if (keyCode == kRightKeyCode) { if ([gCurrentCandidateController isKindOfClass:[VTHorizontalCandidateController class]]) { BOOL updated = [gCurrentCandidateController highlightNextCandidate]; @@ -1195,6 +1245,14 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } + else if (emacsKey == McBpomofoEmacsKeyForward) { + BOOL updated = [gCurrentCandidateController highlightNextCandidate]; + if (!updated) { + [self beep]; + } + [self updateClientComposingBuffer:_currentCandidateClient]; + return YES; + } else if (keyCode == kUpKeyCode) { if ([gCurrentCandidateController isKindOfClass:[VTHorizontalCandidateController class]]) { BOOL updated = [gCurrentCandidateController showPreviousPage]; @@ -1231,7 +1289,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return YES; } } - else if (keyCode == kHomeKeyCode) { + else if (keyCode == kHomeKeyCode || emacsKey == McBpomofoEmacsKeyHome) { if (gCurrentCandidateController.selectedCandidateIndex == 0) { [self beep]; @@ -1243,7 +1301,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [self updateClientComposingBuffer:_currentCandidateClient]; return YES; } - else if (keyCode == kEndKeyCode && [_candidates count] > 0) { + else if ((keyCode == kEndKeyCode || emacsKey == McBpomofoEmacsKeyEnd) && [_candidates count] > 0) { if (gCurrentCandidateController.selectedCandidateIndex == [_candidates count] - 1) { [self beep]; } @@ -1273,7 +1331,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } } if (_inputMode == kPlainBopomofoModeIdentifier) { - string layout = [self currentLayout]; + string layout = [self _currentLayout]; string customPunctuation = string("_punctuation_") + layout + string(1, (char)charCode); string punctuation = string("_punctuation_") + string(1, (char)charCode); From 4c1781d9701f09a84c01a29fe08fded28a51518f Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 19:59:49 +0800 Subject: [PATCH 03/11] Renames half-size to half-width. --- Source/InputMethodController.h | 2 +- Source/InputMethodController.mm | 18 +++++++++--------- Source/en.lproj/Localizable.strings | 2 +- Source/zh-Hant.lproj/Localizable.strings | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/InputMethodController.h b/Source/InputMethodController.h index f1dbaea1..10a18e5a 100644 --- a/Source/InputMethodController.h +++ b/Source/InputMethodController.h @@ -75,6 +75,6 @@ // if Chinese conversion is enabled BOOL _chineseConversionEnabled; - BOOL _halfSizePunctuationEnabled; + BOOL _halfWidthPunctuationEnabled; } @end diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index a501406e..c51bde19 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -78,7 +78,7 @@ static NSString *const kUseHorizontalCandidateListPreferenceKey = @"UseHorizonta static NSString *const kComposingBufferSizePreferenceKey = @"ComposingBufferSize"; static NSString *const kChooseCandidateUsingSpaceKey = @"ChooseCandidateUsingSpaceKey"; static NSString *const kChineseConversionEnabledKey = @"ChineseConversionEnabledKey"; -static NSString *const kHalfSizePunctuationEnabledKey = @"HalfSizePunctuationEnabledKey"; +static NSString *const kHalfWidthPunctuationEnabledKey = @"HalfWidthPunctuationEnabledKey"; static NSString *const kEscToCleanInputBufferKey = @"EscToCleanInputBufferKey"; // advanced (usually optional) settings @@ -200,7 +200,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) _inputMode = kBopomofoModeIdentifier; _chineseConversionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kChineseConversionEnabledKey]; - _halfSizePunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfSizePunctuationEnabledKey]; + _halfWidthPunctuationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:kHalfWidthPunctuationEnabledKey]; } return self; @@ -218,9 +218,9 @@ static double FindHighestScore(const vector& nodes, double epsilon) chineseConversionMenuItem.state = _chineseConversionEnabled ? NSControlStateValueOn : NSControlStateValueOff; [menu addItem:chineseConversionMenuItem]; - NSMenuItem *halfSizePunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Size Punctuations", @"") action:@selector(toggleHalfSizePunctuation:) keyEquivalent:@""]; - halfSizePunctuationMenuItem.state = _halfSizePunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; - [menu addItem:halfSizePunctuationMenuItem]; + NSMenuItem *halfWidthPunctuationMenuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use Half-Width Punctuations", @"") action:@selector(toggleHalfWidthPunctuation:) keyEquivalent:@""]; + halfWidthPunctuationMenuItem.state = _halfWidthPunctuationEnabled ? NSControlStateValueOn : NSControlStateValueOff; + [menu addItem:halfWidthPunctuationMenuItem]; [menu addItem:[NSMenuItem separatorItem]]; [menu addItemWithTitle:NSLocalizedString(@"User Phrases", @"") action:NULL keyEquivalent:@""]; @@ -1112,7 +1112,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 = (_halfSizePunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); + string punctuationNamePrefix = (_halfWidthPunctuationEnabled ? string("_half_punctuation_"): string("_punctuation_")); string customPunctuation = punctuationNamePrefix + layout + string(1, (char)charCode); if ([self _handlePunctuation:customPunctuation usingVerticalMode:useVerticalMode client:client]) { return YES; @@ -1615,10 +1615,10 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } [[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey]; } -- (void)toggleHalfSizePunctuation:(id)sender +- (void)toggleHalfWidthPunctuation:(id)sender { - _halfSizePunctuationEnabled = !_halfSizePunctuationEnabled; - [[NSUserDefaults standardUserDefaults] setBool:_halfSizePunctuationEnabled forKey:kHalfSizePunctuationEnabledKey]; + _halfWidthPunctuationEnabled = !_halfWidthPunctuationEnabled; + [[NSUserDefaults standardUserDefaults] setBool:_halfWidthPunctuationEnabled forKey:kHalfWidthPunctuationEnabledKey]; } diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index 7cfe431d..45b1bba8 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -63,4 +63,4 @@ "Edit Excluded Phrases" = "Edit Excluded Phrases"; -"Use Half-Size Punctuations" = "Use Half-Size Punctuations"; +"Use Half-Width Punctuations" = "Use Half-Width Punctuations"; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index 015c4538..f43d189f 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -63,5 +63,5 @@ "Edit Excluded Phrases" = "編輯要排除的詞彙"; -"Use Half-Size Punctuations" = "使用半型標點符號"; +"Use Half-Width Punctuations" = "使用半型標點符號"; From 366453820d7d74a18146882b2e71fa3dde648fea Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 21:47:52 +0800 Subject: [PATCH 04/11] Adds a tiny tooltop for shift-left/right selections. --- McBopomofo.xcodeproj/project.pbxproj | 9 + .../HorizontalCandidateController.swift | 2 +- Packages/TooltipUI/.gitignore | 7 + Packages/TooltipUI/Package.swift | 25 +++ Packages/TooltipUI/README.md | 3 + .../Sources/TooltipUI/TooltipController.swift | 65 +++++++ Source/InputMethodController.mm | 182 +++++++++++++----- 7 files changed, 239 insertions(+), 54 deletions(-) create mode 100644 Packages/TooltipUI/.gitignore create mode 100644 Packages/TooltipUI/Package.swift create mode 100644 Packages/TooltipUI/README.md create mode 100644 Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 70883d91..99420ef4 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */; }; D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F769278C9E29004A2160 /* CandidateUI */; }; D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; }; + D427F7A927905E90004A2160 /* TooltipUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7A827905E90004A2160 /* TooltipUI */; }; 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 */; }; @@ -168,6 +169,7 @@ D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCCBridge.swift; sourceTree = ""; }; D427F768278C9D0D004A2160 /* CandidateUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = CandidateUI; path = Packages/CandidateUI; sourceTree = ""; }; D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + D427F7A727905E43004A2160 /* TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TooltipUI; path = Packages/TooltipUI; 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 = ""; }; @@ -182,6 +184,7 @@ files = ( 6A38BC2815FC158A00A8A51F /* InputMethodKit.framework in Frameworks */, D48550A325EBE689006A204C /* OpenCC in Frameworks */, + D427F7A927905E90004A2160 /* TooltipUI in Frameworks */, D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */, 6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */, ); @@ -393,6 +396,7 @@ isa = PBXGroup; children = ( D427F768278C9D0D004A2160 /* CandidateUI */, + D427F7A727905E43004A2160 /* TooltipUI */, ); name = Packages; sourceTree = ""; @@ -434,6 +438,7 @@ packageProductDependencies = ( D48550A225EBE689006A204C /* OpenCC */, D427F769278C9E29004A2160 /* CandidateUI */, + D427F7A827905E90004A2160 /* TooltipUI */, ); productName = McBopomofo; productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */; @@ -1059,6 +1064,10 @@ isa = XCSwiftPackageProductDependency; productName = CandidateUI; }; + D427F7A827905E90004A2160 /* TooltipUI */ = { + isa = XCSwiftPackageProductDependency; + productName = TooltipUI; + }; D48550A225EBE689006A204C /* OpenCC */ = { isa = XCSwiftPackageProductDependency; package = D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; diff --git a/Packages/CandidateUI/Sources/CandidateUI/HorizontalCandidateController.swift b/Packages/CandidateUI/Sources/CandidateUI/HorizontalCandidateController.swift index c7d7d505..05d67a42 100644 --- a/Packages/CandidateUI/Sources/CandidateUI/HorizontalCandidateController.swift +++ b/Packages/CandidateUI/Sources/CandidateUI/HorizontalCandidateController.swift @@ -203,7 +203,7 @@ public class HorizontalCandidateController: CandidateController { var contentRect = NSRect(x: 128.0, y: 128.0, width: 0.0, height: 0.0) let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) - panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.hasShadow = true contentRect.origin = NSPoint.zero diff --git a/Packages/TooltipUI/.gitignore b/Packages/TooltipUI/.gitignore new file mode 100644 index 00000000..bb460e7b --- /dev/null +++ b/Packages/TooltipUI/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/TooltipUI/Package.swift b/Packages/TooltipUI/Package.swift new file mode 100644 index 00000000..a708808e --- /dev/null +++ b/Packages/TooltipUI/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "TooltipUI", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "TooltipUI", + targets: ["TooltipUI"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "TooltipUI", + dependencies: []), + ] +) diff --git a/Packages/TooltipUI/README.md b/Packages/TooltipUI/README.md new file mode 100644 index 00000000..94ec7530 --- /dev/null +++ b/Packages/TooltipUI/README.md @@ -0,0 +1,3 @@ +# TooltipUI + +A description of this package. diff --git a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift new file mode 100644 index 00000000..337ac026 --- /dev/null +++ b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift @@ -0,0 +1,65 @@ +import Cocoa + +public class TooltipController: NSWindowController { + let backgroundColor = NSColor(calibratedHue: 0.16, saturation: 0.22, brightness: 0.97, alpha: 1.0) + var messageTextField: NSTextField + var tooltip: String = "" { + didSet { + messageTextField.stringValue = tooltip + adjustSize() + } + } + + public init() { + let contentRect = NSRect(x: 128.0, y: 128.0, width: 300.0, height: 20.0) + let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] + let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) + panel.hasShadow = true + + messageTextField = NSTextField() + messageTextField.isEditable = false + messageTextField.isSelectable = false + messageTextField.isBezeled = false + messageTextField.textColor = .black + messageTextField.drawsBackground = true + messageTextField.backgroundColor = backgroundColor + messageTextField.font = .systemFont(ofSize: NSFont.systemFontSize(for: .small)) + panel.contentView?.addSubview(messageTextField) + + super.init(window: panel) + } + + public required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc(showTooltip:atPoint:) + public func show(tooltip: String, at point: NSPoint) { + self.tooltip = tooltip + window?.orderFront(nil) + set(windowLocation: point) + } + + @objc + public func hide() { + window?.orderOut(nil) + } + + private func set(windowLocation location: NSPoint) { + var newPoint = location + if location.y > 5 { + newPoint.y -= 5 + } + window?.setFrameTopLeftPoint(newPoint) + } + + private func adjustSize() { + let attrString = messageTextField.attributedStringValue; + var rect = attrString.boundingRect(with: NSSize(width: 1600.0, height: 1600.0), options: .usesLineFragmentOrigin) + rect.size.width += 10 + messageTextField.frame = rect + window?.setFrame(rect, display: true) + } + +} diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index c51bde19..dc8436ba 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -42,6 +42,7 @@ #import "McBopomofo-Swift.h" @import CandidateUI; +@import TooltipUI; @import OpenCC; // C++ namespace usages @@ -489,6 +490,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // i.e. the client app needs to take care of where to put ths composing buffer [client setMarkedText:attrString selectionRange:NSMakeRange((NSInteger)_builder->markerCursorIndex(), 0) replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; _latestReadingCursor = (NSInteger)_builder->markerCursorIndex(); + [self _showCurrentMarkedTextTooltipWithClient:client]; } else { // we must use NSAttributedString so that the cursor is visible -- @@ -501,6 +503,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // i.e. the client app needs to take care of where to put ths composing buffer [client setMarkedText:attrString selectionRange:NSMakeRange(cursorIndex, 0) replacementRange:NSMakeRange(NSNotFound, NSNotFound)]; _latestReadingCursor = cursorIndex; + [self _hideTooltip]; } } @@ -606,47 +609,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } return layout; } -- (NSString *)_currentMarkedText -{ - if (_builder->markerCursorIndex() < 0) { - return @""; - } - if (!_bpmfReadingBuffer->isEmpty()) { - return @""; - } - - size_t begin = min(_builder->markerCursorIndex(), _builder->cursorIndex()); - size_t end = max(_builder->markerCursorIndex(), _builder->cursorIndex()); - // A phrase should contian at least two characters. - if (end - begin < 2) { - return @""; - } - - NSRange range = NSMakeRange((NSInteger)begin, (NSInteger)(end - begin)); - NSString *reading = [_composingBuffer substringWithRange:range]; - NSMutableString *string = [[NSMutableString alloc] init]; - [string appendString:reading]; - [string appendString:@" "]; - NSMutableArray *readingsArray = [[NSMutableArray alloc] init]; - vector v = _builder->readingsAtRange(begin, end); - for(vector::iterator it_i=v.begin(); it_i!=v.end(); ++it_i) { - [readingsArray addObject:[NSString stringWithUTF8String:it_i->c_str()]]; - } - [string appendString:[readingsArray componentsJoinedByString:@"-"]]; - return string; -} - -- (BOOL)_writeUserPhrase -{ - NSString *currentMarkedPhrase = [self _currentMarkedText]; - if (![currentMarkedPhrase length]) { - return NO; - } - - return [LanguageModelManager writeUserPhrase:currentMarkedPhrase]; -} - -- (McBpomofoEmacsKey)detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags +- (McBpomofoEmacsKey)_detectEmacsKeyFromCharCode:(UniChar)charCode modifiers:(NSUInteger)flags { if (flags & NSControlKeyMask) { char c = charCode + 'a' - 1; @@ -698,7 +661,7 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } // get the unicode character code UniChar charCode = [inputText length] ? [inputText characterAtIndex:0] : 0; - McBpomofoEmacsKey emacsKey = [self detectEmacsKeyFromCharCode:charCode modifiers:flags]; + McBpomofoEmacsKey emacsKey = [self _detectEmacsKeyFromCharCode:charCode modifiers:flags]; if ([[client bundleIdentifier] isEqualToString:@"com.apple.Terminal"] && [NSStringFromClass([client class]) isEqualToString:@"IPMDServerClientWrapper"]) { // special handling for com.apple.Terminal @@ -1400,24 +1363,30 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } + (VTHorizontalCandidateController *)horizontalCandidateController { static VTHorizontalCandidateController *instance = nil; - @synchronized(self) { - if (!instance) { - instance = [[VTHorizontalCandidateController alloc] init]; - } - } - + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[VTHorizontalCandidateController alloc] init]; + }); return instance; } + (VTVerticalCandidateController *)verticalCandidateController { static VTVerticalCandidateController *instance = nil; - @synchronized(self) { - if (!instance) { - instance = [[VTVerticalCandidateController alloc] init]; - } - } + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[VTVerticalCandidateController alloc] init]; + }); + return instance; +} ++ (TooltipController *)tooltipController +{ + static TooltipController *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [[TooltipController alloc] init]; + }); return instance; } @@ -1541,6 +1510,113 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } gCurrentCandidateController.visible = YES; } +#pragma mark - User phrases + +- (NSString *)_currentMarkedText +{ + if (_builder->markerCursorIndex() < 0) { + return @""; + } + if (!_bpmfReadingBuffer->isEmpty()) { + return @""; + } + + size_t begin = min(_builder->markerCursorIndex(), _builder->cursorIndex()); + size_t end = max(_builder->markerCursorIndex(), _builder->cursorIndex()); + // A phrase should contian at least two characters. + if (end - begin < 1) { + return @""; + } + + NSRange range = NSMakeRange((NSInteger)begin, (NSInteger)(end - begin)); + NSString *selectedText = [_composingBuffer substringWithRange:range]; + return selectedText; +} + +- (NSString *)_currentMarkedTextAndReadings +{ + if (_builder->markerCursorIndex() < 0) { + return @""; + } + if (!_bpmfReadingBuffer->isEmpty()) { + return @""; + } + + size_t begin = min(_builder->markerCursorIndex(), _builder->cursorIndex()); + size_t end = max(_builder->markerCursorIndex(), _builder->cursorIndex()); + // A phrase should contian at least two characters. + if (end - begin < 2) { + return @""; + } + + NSRange range = NSMakeRange((NSInteger)begin, (NSInteger)(end - begin)); + NSString *selectedText = [_composingBuffer substringWithRange:range]; + NSMutableString *string = [[NSMutableString alloc] init]; + [string appendString:selectedText]; + [string appendString:@" "]; + NSMutableArray *readingsArray = [[NSMutableArray alloc] init]; + vector v = _builder->readingsAtRange(begin, end); + for(vector::iterator it_i=v.begin(); it_i!=v.end(); ++it_i) { + [readingsArray addObject:[NSString stringWithUTF8String:it_i->c_str()]]; + } + [string appendString:[readingsArray componentsJoinedByString:@"-"]]; + return string; +} + +- (BOOL)_writeUserPhrase +{ + NSString *currentMarkedPhrase = [self _currentMarkedTextAndReadings]; + if (![currentMarkedPhrase length]) { + return NO; + } + + return [LanguageModelManager writeUserPhrase:currentMarkedPhrase]; +} + +- (void)_showCurrentMarkedTextTooltipWithClient:(id)client +{ + NSString *text = [self _currentMarkedText]; + NSInteger length = text.length; + if (!length) { + [self _hideTooltip]; + } + else if (length == 1) { + NSString *messsage = [NSString stringWithFormat:NSLocalizedString(@"You are now selecting \"%@\". You can add a phrase with two or more characters.", @""), text]; + [self _showTooltip:messsage client:client]; + } + else { + NSString *messsage = [NSString stringWithFormat:NSLocalizedString(@"You are now selecting \"%@\". Press enter to add a new phrase.", @""), text]; + [self _showTooltip:messsage client:client]; + } +} + +- (void)_showTooltip:(NSString *)tooltip client:(id)client +{ + NSRect lineHeightRect = NSMakeRect(0.0, 0.0, 16.0, 16.0); + + NSInteger cursor = _latestReadingCursor; + if (cursor == [_composingBuffer length] && cursor != 0) { + cursor--; + } + + // some apps (e.g. Twitter for Mac's search bar) handle this call incorrectly, hence the try-catch + @try { + [client attributesForCharacterIndex:cursor lineHeightRectangle:&lineHeightRect]; + } + @catch (NSException *exception) { + NSLog(@"%@", exception); + } + + [[McBopomofoInputMethodController tooltipController] showTooltip:tooltip atPoint:lineHeightRect.origin]; +} + +- (void)_hideTooltip +{ + if ([McBopomofoInputMethodController tooltipController].window.isVisible) { + [[McBopomofoInputMethodController tooltipController] hide]; + } +} + #pragma mark - Misc menu items - (void)showPreferences:(id)sender From a7e38b5b2d130be77daee84ac34af23cbb7b4302 Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 22:00:29 +0800 Subject: [PATCH 05/11] Fine-tunes tooltip UI. --- Source/InputMethodController.mm | 3 +++ Source/en.lproj/Localizable.strings | 4 ++++ Source/zh-Hant.lproj/Localizable.strings | 3 +++ 3 files changed, 10 insertions(+) diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index dc8436ba..09439089 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -336,6 +336,8 @@ static double FindHighestScore(const vector& nodes, double epsilon) gCurrentCandidateController.delegate = nil; gCurrentCandidateController.visible = NO; [_candidates removeAllObjects]; + + [self _hideTooltip]; } - (void)setValue:(id)value forTag:(long)tag client:(id)sender @@ -411,6 +413,7 @@ static double FindHighestScore(const vector& nodes, double epsilon) [_composingBuffer setString:@""]; gCurrentCandidateController.visible = NO; [_candidates removeAllObjects]; + [self _hideTooltip]; } NS_INLINE size_t min(size_t a, size_t b) { return a < b ? a : b; } diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index 45b1bba8..0a96077b 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -64,3 +64,7 @@ "Edit Excluded Phrases" = "Edit Excluded Phrases"; "Use Half-Width Punctuations" = "Use Half-Width Punctuations"; + +"You are now selecting \"%@\". You can add a phrase with two or more characters." = "You are now selecting \"%@\". You can add a phrase with two or more characters."; + +"You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase."; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index f43d189f..a91f7bc9 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -65,3 +65,6 @@ "Use Half-Width Punctuations" = "使用半型標點符號"; +"You are now selecting \"%@\". You can add a phrase with two or more characters." = "您目前選擇了 \"%@\"。請選擇兩個字以上,才能加入使用者詞彙。"; + +"You are now selecting \"%@\". Press enter to add a new phrase." = "您目前選擇了 \"%@\"。按下 Enter 就可以加入到使用者詞彙中。"; From d4772ffa992ce40accabc4a5547ce4d2e9ec18fc Mon Sep 17 00:00:00 2001 From: zonble Date: Thu, 13 Jan 2022 23:38:56 +0800 Subject: [PATCH 06/11] Adds notifier UI to notify user Chinese conversion on/off. --- McBopomofo.xcodeproj/project.pbxproj | 9 + Packages/NotifierUI/.gitignore | 7 + Packages/NotifierUI/Package.swift | 25 +++ Packages/NotifierUI/README.md | 3 + .../NotifierUI/NotifierController.swift | 171 ++++++++++++++++++ .../Sources/TooltipUI/TooltipController.swift | 2 +- Source/InputMethodController.mm | 6 + Source/en.lproj/Localizable.strings | 4 + Source/zh-Hant.lproj/Localizable.strings | 4 + 9 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 Packages/NotifierUI/.gitignore create mode 100644 Packages/NotifierUI/Package.swift create mode 100644 Packages/NotifierUI/README.md create mode 100644 Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index 99420ef4..bd96b94c 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F769278C9E29004A2160 /* CandidateUI */; }; D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; }; D427F7A927905E90004A2160 /* TooltipUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7A827905E90004A2160 /* TooltipUI */; }; + D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7AD27907B8A004A2160 /* NotifierUI */; }; 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 */; }; @@ -170,6 +171,7 @@ D427F768278C9D0D004A2160 /* CandidateUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = CandidateUI; path = Packages/CandidateUI; sourceTree = ""; }; D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; D427F7A727905E43004A2160 /* TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TooltipUI; path = Packages/TooltipUI; sourceTree = ""; }; + D427F7AC27907B7E004A2160 /* NotifierUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NotifierUI; path = Packages/NotifierUI; 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 = ""; }; @@ -186,6 +188,7 @@ D48550A325EBE689006A204C /* OpenCC in Frameworks */, D427F7A927905E90004A2160 /* TooltipUI in Frameworks */, D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */, + D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */, 6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -397,6 +400,7 @@ children = ( D427F768278C9D0D004A2160 /* CandidateUI */, D427F7A727905E43004A2160 /* TooltipUI */, + D427F7AC27907B7E004A2160 /* NotifierUI */, ); name = Packages; sourceTree = ""; @@ -439,6 +443,7 @@ D48550A225EBE689006A204C /* OpenCC */, D427F769278C9E29004A2160 /* CandidateUI */, D427F7A827905E90004A2160 /* TooltipUI */, + D427F7AD27907B8A004A2160 /* NotifierUI */, ); productName = McBopomofo; productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */; @@ -1068,6 +1073,10 @@ isa = XCSwiftPackageProductDependency; productName = TooltipUI; }; + D427F7AD27907B8A004A2160 /* NotifierUI */ = { + isa = XCSwiftPackageProductDependency; + productName = NotifierUI; + }; D48550A225EBE689006A204C /* OpenCC */ = { isa = XCSwiftPackageProductDependency; package = D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; diff --git a/Packages/NotifierUI/.gitignore b/Packages/NotifierUI/.gitignore new file mode 100644 index 00000000..bb460e7b --- /dev/null +++ b/Packages/NotifierUI/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/NotifierUI/Package.swift b/Packages/NotifierUI/Package.swift new file mode 100644 index 00000000..c25baecb --- /dev/null +++ b/Packages/NotifierUI/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NotifierUI", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "NotifierUI", + targets: ["NotifierUI"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "NotifierUI", + dependencies: []), + ] +) diff --git a/Packages/NotifierUI/README.md b/Packages/NotifierUI/README.md new file mode 100644 index 00000000..51230c35 --- /dev/null +++ b/Packages/NotifierUI/README.md @@ -0,0 +1,3 @@ +# NotifierUI + +A description of this package. diff --git a/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift b/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift new file mode 100644 index 00000000..ae1bedc9 --- /dev/null +++ b/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift @@ -0,0 +1,171 @@ +import Cocoa + +protocol NotifierWindowDelegate: AnyObject { + func windowDidBecomeClicked(_ window: NotifierWindow) +} + +class NotifierWindow: NSWindow { + weak var clickDelegate: NotifierWindowDelegate? + + override func mouseDown(with event: NSEvent) { + clickDelegate?.windowDidBecomeClicked(self) + } +} + +let kWindowWidth: CGFloat = 160.0 +let kWindowHeight: CGFloat = 80.0 + +public class NotifierController: NSWindowController, NotifierWindowDelegate { + private var messageTextField: NSTextField + + private var message: String = "" { + didSet { + let paraStyle = NSMutableParagraphStyle() + paraStyle.setParagraphStyle(NSParagraphStyle.default) + paraStyle.alignment = .center + let attr: [NSAttributedString.Key: AnyObject] = [ + .foregroundColor: foregroundColor, + .font: NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .regular)), + .paragraphStyle: paraStyle + ] + let attrString = NSAttributedString(string: message, attributes: attr) + messageTextField.attributedStringValue = attrString + let width = window?.frame.width ?? kWindowWidth + let rect = attrString.boundingRect(with: NSSize(width: width, height: 1600), options: .usesLineFragmentOrigin) + let height = rect.height + let x = messageTextField.frame.origin.x + let y = ((window?.frame.height ?? kWindowHeight) - height) / 2 + let newFrame = NSRect(x: x, y: y, width: width, height: height) + messageTextField.frame = newFrame + } + } + private var shouldStay: Bool = false + private var backgroundColor: NSColor = .black { + didSet { + self.window?.backgroundColor = backgroundColor + self.messageTextField.backgroundColor = backgroundColor + } + } + private var foregroundColor: NSColor = .white { + didSet { + self.messageTextField.textColor = foregroundColor + } + } + private var waitTimer: Timer? + private var fadeTimer: Timer? + + private static var instanceCount = 0 + private static var lastLocation = NSPoint.zero + + @objc public static func notify(message: String, stay: Bool = false) { + let controller = NotifierController() + controller.message = message + controller.shouldStay = stay + controller.show() + } + + static func increaseInstanceCount() { + instanceCount += 1 + } + + static func decreaseInstanceCount() { + instanceCount -= 1 + if instanceCount < 0 { + instanceCount = 0 + } + } + + public init() { + let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero + let contentRect = NSRect(x: 0, y: 0, width: kWindowWidth, height: kWindowHeight) + var windowRect = contentRect + windowRect.origin.x = screenRect.maxX - windowRect.width - 10 + windowRect.origin.y = screenRect.maxY - windowRect.height - 10 + let styleMask: NSWindow.StyleMask = [.borderless] + let panel = NotifierWindow(contentRect: windowRect, styleMask: styleMask, backing: .buffered, defer: false) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) + panel.hasShadow = true + panel.backgroundColor = backgroundColor + + messageTextField = NSTextField() + messageTextField.frame = contentRect + messageTextField.isEditable = false + messageTextField.isSelectable = false + messageTextField.isBezeled = false + messageTextField.textColor = foregroundColor + messageTextField.drawsBackground = true + messageTextField.backgroundColor = backgroundColor + messageTextField.font = .systemFont(ofSize: NSFont.systemFontSize(for: .small)) + panel.contentView?.addSubview(messageTextField) + + super.init(window: panel) + + panel.clickDelegate = self + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setStartLocation() { + if NotifierController.instanceCount == 0 { + return + } + let lastLocation = NotifierController.lastLocation + let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero + var windowRect = self.window?.frame ?? NSRect.zero + windowRect.origin.x = lastLocation.x + windowRect.origin.y = lastLocation.y - 10 - windowRect.height + + if windowRect.origin.y < screenRect.minY { + return + } + + self.window?.setFrame(windowRect, display: true) + } + + func moveIn() { + let afterRect = self.window?.frame ?? NSRect.zero + NotifierController.lastLocation = afterRect.origin + var beforeRect = afterRect + beforeRect.origin.y += 10 + window?.setFrame(beforeRect, display: true) + window?.orderFront(self) + window?.setFrame(afterRect, display: true, animate: true) + } + + func show() { + setStartLocation() + moveIn() + NotifierController.increaseInstanceCount() + waitTimer = Timer.scheduledTimer(timeInterval: shouldStay ? 5 : 1, target: self, selector: #selector(fadeOut), userInfo: nil, repeats: false) + } + + @objc func doFadeOut(_ timer: Timer) { + let opacity = self.window?.alphaValue ?? 0 + if opacity <= 0 { + self.close() + } else { + self.window?.alphaValue = opacity - 0.2 + } + } + + @objc func fadeOut() { + waitTimer?.invalidate() + waitTimer = nil + NotifierController.decreaseInstanceCount() + fadeTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(doFadeOut(_:)), userInfo: nil, repeats: true) + } + + public override func close() { + waitTimer?.invalidate() + waitTimer = nil + fadeTimer?.invalidate() + fadeTimer = nil + super.close() + } + + func windowDidBecomeClicked(_ window: NotifierWindow) { + self.fadeOut() + } +} diff --git a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift index 337ac026..0103ec12 100644 --- a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift +++ b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift @@ -14,7 +14,7 @@ public class TooltipController: NSWindowController { let contentRect = NSRect(x: 128.0, y: 128.0, width: 300.0, height: 20.0) let styleMask: NSWindow.StyleMask = [.borderless, .nonactivatingPanel] let panel = NSPanel(contentRect: contentRect, styleMask: styleMask, backing: .buffered, defer: false) - panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel)) + panel.level = NSWindow.Level(Int(kCGPopUpMenuWindowLevel) + 1) panel.hasShadow = true messageTextField = NSTextField() diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 09439089..1b02accd 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -42,6 +42,7 @@ #import "McBopomofo-Swift.h" @import CandidateUI; +@import NotifierUI; @import TooltipUI; @import OpenCC; @@ -1692,6 +1693,11 @@ NS_INLINE size_t max(size_t a, size_t b) { return a > b ? a : b; } { _chineseConversionEnabled = !_chineseConversionEnabled; [[NSUserDefaults standardUserDefaults] setBool:_chineseConversionEnabled forKey:kChineseConversionEnabledKey]; + + [NotifierController notifyWithMessage: + _chineseConversionEnabled ? + NSLocalizedString(@"Chinese conversion on", @"") : + NSLocalizedString(@"Chinese conversion off", @"") stay:NO]; } - (void)toggleHalfWidthPunctuation:(id)sender diff --git a/Source/en.lproj/Localizable.strings b/Source/en.lproj/Localizable.strings index 0a96077b..7aa8d586 100644 --- a/Source/en.lproj/Localizable.strings +++ b/Source/en.lproj/Localizable.strings @@ -68,3 +68,7 @@ "You are now selecting \"%@\". You can add a phrase with two or more characters." = "You are now selecting \"%@\". You can add a phrase with two or more characters."; "You are now selecting \"%@\". Press enter to add a new phrase." = "You are now selecting \"%@\". Press enter to add a new phrase."; + +"Chinese conversion on" = "Chinese conversion on"; + +"Chinese conversion off" = "Chinese conversion off"; diff --git a/Source/zh-Hant.lproj/Localizable.strings b/Source/zh-Hant.lproj/Localizable.strings index a91f7bc9..98664234 100644 --- a/Source/zh-Hant.lproj/Localizable.strings +++ b/Source/zh-Hant.lproj/Localizable.strings @@ -68,3 +68,7 @@ "You are now selecting \"%@\". You can add a phrase with two or more characters." = "您目前選擇了 \"%@\"。請選擇兩個字以上,才能加入使用者詞彙。"; "You are now selecting \"%@\". Press enter to add a new phrase." = "您目前選擇了 \"%@\"。按下 Enter 就可以加入到使用者詞彙中。"; + +"Chinese conversion on" = "已經切換到簡體中文模式"; + +"Chinese conversion off" = "已經切換到繁體中文模式"; From e01eb46c9f2f8861547cf7658f2406b10097bcdc Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 00:43:21 +0800 Subject: [PATCH 07/11] Wraps InputSourceHelper to a SPM package. --- McBopomofo.xcodeproj/project.pbxproj | 48 +++--- Packages/InputSourceHelper/.gitignore | 7 + Packages/InputSourceHelper/Package.swift | 25 +++ Packages/InputSourceHelper/README.md | 3 + .../InputSourceHelper.swift | 0 Packages/NonModalAlertWindow/.gitignore | 7 + Packages/NonModalAlertWindow/Package.swift | 25 +++ Packages/NonModalAlertWindow/README.md | 3 + .../NonModalAlertWindowController.swift | 147 ++++++++++++++++++ .../NonModalAlertWindowController.xib | 85 ++++++++++ Source/InputMethodController.mm | 1 - Source/Installer/AppDelegate.m | 3 +- Source/OpenCCBridge.swift | 8 +- Source/main.m | 2 +- 14 files changed, 337 insertions(+), 27 deletions(-) create mode 100644 Packages/InputSourceHelper/.gitignore create mode 100644 Packages/InputSourceHelper/Package.swift create mode 100644 Packages/InputSourceHelper/README.md rename {Source => Packages/InputSourceHelper/Sources/InputSourceHelper}/InputSourceHelper.swift (100%) create mode 100644 Packages/NonModalAlertWindow/.gitignore create mode 100644 Packages/NonModalAlertWindow/Package.swift create mode 100644 Packages/NonModalAlertWindow/README.md create mode 100644 Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift create mode 100644 Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index bd96b94c..af6e167a 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -39,17 +39,17 @@ D41355D8278D74B5005E5CBD /* LanguageModelManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = D41355D7278D7409005E5CBD /* LanguageModelManager.mm */; }; D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D41355D9278E6D17005E5CBD /* McBopomofoLM.cpp */; }; D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D41355DC278EA3ED005E5CBD /* UserPhrasesLM.cpp */; }; - D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */; }; D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F769278C9E29004A2160 /* CandidateUI */; }; D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F76B278CA1BA004A2160 /* AppDelegate.swift */; }; D427F7A927905E90004A2160 /* TooltipUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7A827905E90004A2160 /* TooltipUI */; }; D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7AD27907B8A004A2160 /* NotifierUI */; }; + D427F7B4279086DC004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B3279086DC004A2160 /* InputSourceHelper */; }; + D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; }; + D427F7BB27908D78004A2160 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F7BA27908D78004A2160 /* OpenCCBridge.swift */; }; + D427F7BE27908DD7004A2160 /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7BD27908DD7004A2160 /* OpenCC */; }; 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 */; }; - D47F7DD5278C25A0002F9DD7 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD4278C25A0002F9DD7 /* InputSourceHelper.swift */; }; - D47F7DD6278C3075002F9DD7 /* InputSourceHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D47F7DD4278C25A0002F9DD7 /* InputSourceHelper.swift */; }; - D48550A325EBE689006A204C /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = D48550A225EBE689006A204C /* OpenCC */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -167,16 +167,16 @@ D41355DC278EA3ED005E5CBD /* UserPhrasesLM.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = UserPhrasesLM.cpp; sourceTree = ""; }; D41355DD278EA3ED005E5CBD /* UserPhrasesLM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserPhrasesLM.h; sourceTree = ""; }; D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "McBopomofo-Bridging-Header.h"; sourceTree = ""; }; - D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCCBridge.swift; sourceTree = ""; }; D427F768278C9D0D004A2160 /* CandidateUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = CandidateUI; path = Packages/CandidateUI; sourceTree = ""; }; D427F76B278CA1BA004A2160 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; D427F7A727905E43004A2160 /* TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TooltipUI; path = Packages/TooltipUI; sourceTree = ""; }; D427F7AC27907B7E004A2160 /* NotifierUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NotifierUI; path = Packages/NotifierUI; sourceTree = ""; }; + D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = ""; }; + D427F7BA27908D78004A2160 /* OpenCCBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenCCBridge.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 = ""; }; D47F7DD2278C1263002F9DD7 /* UserOverrideModel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserOverrideModel.cpp; sourceTree = ""; }; - D47F7DD4278C25A0002F9DD7 /* InputSourceHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputSourceHelper.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -184,11 +184,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D427F7B4279086DC004A2160 /* InputSourceHelper in Frameworks */, 6A38BC2815FC158A00A8A51F /* InputMethodKit.framework in Frameworks */, - D48550A325EBE689006A204C /* OpenCC in Frameworks */, D427F7A927905E90004A2160 /* TooltipUI in Frameworks */, D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */, D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */, + D427F7BE27908DD7004A2160 /* OpenCC in Frameworks */, 6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -197,6 +198,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */, 6ACA41CD15FC1D7500935EF6 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -247,10 +249,9 @@ D41355D7278D7409005E5CBD /* LanguageModelManager.mm */, 6A0D4EC815FC0D6400ABF4B3 /* main.m */, D427F76B278CA1BA004A2160 /* AppDelegate.swift */, - D47F7DD4278C25A0002F9DD7 /* InputSourceHelper.swift */, D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */, - D427A9C025ED28CC005D43E0 /* OpenCCBridge.swift */, + D427F7BA27908D78004A2160 /* OpenCCBridge.swift */, 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */, ); @@ -401,6 +402,7 @@ D427F768278C9D0D004A2160 /* CandidateUI */, D427F7A727905E43004A2160 /* TooltipUI */, D427F7AC27907B7E004A2160 /* NotifierUI */, + D427F7B2279086B5004A2160 /* InputSourceHelper */, ); name = Packages; sourceTree = ""; @@ -440,10 +442,11 @@ ); name = McBopomofo; packageProductDependencies = ( - D48550A225EBE689006A204C /* OpenCC */, D427F769278C9E29004A2160 /* CandidateUI */, D427F7A827905E90004A2160 /* TooltipUI */, D427F7AD27907B8A004A2160 /* NotifierUI */, + D427F7B3279086DC004A2160 /* InputSourceHelper */, + D427F7BD27908DD7004A2160 /* OpenCC */, ); productName = McBopomofo; productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */; @@ -464,6 +467,9 @@ 6ACA420115FC1DCC00935EF6 /* PBXTargetDependency */, ); name = McBopomofoInstaller; + packageProductDependencies = ( + D427F7B5279086F6004A2160 /* InputSourceHelper */, + ); productName = McBopomofoInstaller; productReference = 6ACA41CB15FC1D7500935EF6 /* McBopomofoInstaller.app */; productType = "com.apple.product-type.application"; @@ -492,7 +498,7 @@ ); mainGroup = 6A0D4E9215FC0CFA00ABF4B3; packageReferences = ( - D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */, + D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */, ); productRefGroup = 6A0D4EA315FC0D2D00ABF4B3 /* Products */; projectDirPath = ""; @@ -568,12 +574,11 @@ files = ( D427F76C278CA2B0004A2160 /* AppDelegate.swift in Sources */, 6A0D4ED315FC0D6400ABF4B3 /* main.m in Sources */, - D47F7DD5278C25A0002F9DD7 /* InputSourceHelper.swift in Sources */, D47F7DD0278C0897002F9DD7 /* NonModalAlertWindowController.swift in Sources */, - D427A9C125ED28CC005D43E0 /* OpenCCBridge.swift in Sources */, D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */, 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */, D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */, + D427F7BB27908D78004A2160 /* OpenCCBridge.swift in Sources */, D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */, @@ -589,7 +594,6 @@ 6ACA41F915FC1D9000935EF6 /* AppDelegate.m in Sources */, 6A225A232367A1D700F685C6 /* ArchiveUtil.m in Sources */, 6ACA41FF15FC1D9000935EF6 /* main.m in Sources */, - D47F7DD6278C3075002F9DD7 /* InputSourceHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1054,12 +1058,12 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */ = { + D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ddddxxx/SwiftyOpenCC.git"; requirement = { kind = revision; - revision = 1d8105a0f7199c90af722bff62728050c858e777; + revision = a802c02cbf1c6fcd529575f11a9876d94fc813f4; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -1077,9 +1081,17 @@ isa = XCSwiftPackageProductDependency; productName = NotifierUI; }; - D48550A225EBE689006A204C /* OpenCC */ = { + D427F7B3279086DC004A2160 /* InputSourceHelper */ = { isa = XCSwiftPackageProductDependency; - package = D48550A125EBE689006A204C /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; + productName = InputSourceHelper; + }; + D427F7B5279086F6004A2160 /* InputSourceHelper */ = { + isa = XCSwiftPackageProductDependency; + productName = InputSourceHelper; + }; + D427F7BD27908DD7004A2160 /* OpenCC */ = { + isa = XCSwiftPackageProductDependency; + package = D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; productName = OpenCC; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Packages/InputSourceHelper/.gitignore b/Packages/InputSourceHelper/.gitignore new file mode 100644 index 00000000..bb460e7b --- /dev/null +++ b/Packages/InputSourceHelper/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/InputSourceHelper/Package.swift b/Packages/InputSourceHelper/Package.swift new file mode 100644 index 00000000..4543d050 --- /dev/null +++ b/Packages/InputSourceHelper/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "InputSourceHelper", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "InputSourceHelper", + targets: ["InputSourceHelper"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "InputSourceHelper", + dependencies: []), + ] +) diff --git a/Packages/InputSourceHelper/README.md b/Packages/InputSourceHelper/README.md new file mode 100644 index 00000000..cc27cfb9 --- /dev/null +++ b/Packages/InputSourceHelper/README.md @@ -0,0 +1,3 @@ +# InputSourceHelper + +A description of this package. diff --git a/Source/InputSourceHelper.swift b/Packages/InputSourceHelper/Sources/InputSourceHelper/InputSourceHelper.swift similarity index 100% rename from Source/InputSourceHelper.swift rename to Packages/InputSourceHelper/Sources/InputSourceHelper/InputSourceHelper.swift diff --git a/Packages/NonModalAlertWindow/.gitignore b/Packages/NonModalAlertWindow/.gitignore new file mode 100644 index 00000000..bb460e7b --- /dev/null +++ b/Packages/NonModalAlertWindow/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/NonModalAlertWindow/Package.swift b/Packages/NonModalAlertWindow/Package.swift new file mode 100644 index 00000000..7e9a84ed --- /dev/null +++ b/Packages/NonModalAlertWindow/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "NonModalAlertWindow", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "NonModalAlertWindow", + targets: ["NonModalAlertWindow"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "NonModalAlertWindow", + dependencies: []), + ] +) diff --git a/Packages/NonModalAlertWindow/README.md b/Packages/NonModalAlertWindow/README.md new file mode 100644 index 00000000..93b2b884 --- /dev/null +++ b/Packages/NonModalAlertWindow/README.md @@ -0,0 +1,3 @@ +# NonModalAlertWindow + +A description of this package. diff --git a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift new file mode 100644 index 00000000..936250e9 --- /dev/null +++ b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift @@ -0,0 +1,147 @@ +// +// NonModalAlertWindowController.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 + +@objc public protocol NonModalAlertWindowControllerDelegate: AnyObject { + func nonModalAlertWindowControllerDidConfirm(_ controller: NonModalAlertWindowController) + func nonModalAlertWindowControllerDidCancel(_ controller: NonModalAlertWindowController) +} + +@objc public class NonModalAlertWindowController: NSWindowController { + @objc (sharedInstance) + public static let shared: NonModalAlertWindowController = { + NSLog("shaerd???") + guard let path = Bundle.main.path(forResource: "NonModalAlertWindow_NonModalAlertWindow", ofType: "bundle"), + let modelBudle = Bundle(path: path), + let xibPath = modelBudle.path(forResource: "NonModalAlertWindowController", ofType: "nib") + else { + let controller = NonModalAlertWindowController(windowNibName: "NonModalAlertWindowController") + return controller + } + NSLog("shared path \(path)") + NSLog("shared modelBudle \(modelBudle)") + NSLog("shared xibPath \(xibPath)") + let controller = NonModalAlertWindowController(windowNibPath: xibPath, owner: self) + return controller + }() + + @IBOutlet weak var titleTextField: NSTextField! + @IBOutlet weak var contentTextField: NSTextField! + @IBOutlet weak var confirmButton: NSButton! + @IBOutlet weak var cancelButton: NSButton! + weak var delegate: NonModalAlertWindowControllerDelegate? + + @objc public func show(title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?, cancelAsDefault: Bool, delegate: NonModalAlertWindowControllerDelegate?) { + if window?.isVisible == true { + self.delegate?.nonModalAlertWindowControllerDidCancel(self) + } + + self.delegate = delegate + + var oldFrame = confirmButton.frame + confirmButton.title = confirmButtonTitle + confirmButton.sizeToFit() + + var newFrame = confirmButton.frame + newFrame.size.width = max(90, newFrame.size.width + 10) + newFrame.origin.x += oldFrame.size.width - newFrame.size.width + confirmButton.frame = newFrame + + if let cancelButtonTitle = cancelButtonTitle { + cancelButton.title = cancelButtonTitle + cancelButton.sizeToFit() + var adjustFrame = cancelButton.frame + adjustFrame.size.width = max(90, adjustFrame.size.width + 10) + adjustFrame.origin.x = newFrame.origin.x - adjustFrame.size.width + confirmButton.frame = adjustFrame + cancelButton.isHidden = false + } else { + cancelButton.isHidden = true + } + + cancelButton.nextKeyView = confirmButton + confirmButton.nextKeyView = cancelButton + + if cancelButtonTitle != nil { + if cancelAsDefault { + window?.defaultButtonCell = cancelButton.cell as? NSButtonCell + } else { + cancelButton.keyEquivalent = " " + window?.defaultButtonCell = confirmButton.cell as? NSButtonCell + } + } else { + window?.defaultButtonCell = confirmButton.cell as? NSButtonCell + } + + titleTextField.stringValue = title + + oldFrame = contentTextField.frame + contentTextField.stringValue = content + + var infiniteHeightFrame = oldFrame + infiniteHeightFrame.size.width -= 4.0 + infiniteHeightFrame.size.height = 10240 + newFrame = (content as NSString).boundingRect(with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin], attributes: [.font: contentTextField.font!]) + newFrame.size.width = max(newFrame.size.width, oldFrame.size.width) + newFrame.size.height += 4.0 + newFrame.origin = oldFrame.origin + newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height) + contentTextField.frame = newFrame + + var windowFrame = window?.frame ?? NSRect.zero + windowFrame.size.height += (newFrame.size.height - oldFrame.size.height) + window?.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1) + window?.setFrame(windowFrame, display: true) + window?.center() + window?.makeKeyAndOrderFront(self) + NSApp.activate(ignoringOtherApps: true) + } + + @IBAction func confirmButtonAction(_ sender: Any) { + delegate?.nonModalAlertWindowControllerDidConfirm(self) + window?.orderOut(self) + } + + @IBAction func cancelButtonAction(_ sender: Any) { + cancel(sender) + } + + @objc public func cancel(_ sender: Any) { + delegate?.nonModalAlertWindowControllerDidCancel(self) + delegate = nil + window?.orderOut(self) + } + +} diff --git a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib new file mode 100644 index 00000000..8c378007 --- /dev/null +++ b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 1b02accd..9d4cfb11 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -44,7 +44,6 @@ @import CandidateUI; @import NotifierUI; @import TooltipUI; -@import OpenCC; // C++ namespace usages using namespace std; diff --git a/Source/Installer/AppDelegate.m b/Source/Installer/AppDelegate.m index 62f7acc5..84dbd224 100644 --- a/Source/Installer/AppDelegate.m +++ b/Source/Installer/AppDelegate.m @@ -27,7 +27,8 @@ #import "AppDelegate.h" #import -#import "McBopomofoInstaller-Swift.h" +//#import "McBopomofoInstaller-Swift.h" +@import InputSourceHelper; static NSString *const kTargetBin = @"McBopomofo"; static NSString *const kTargetType = @"app"; diff --git a/Source/OpenCCBridge.swift b/Source/OpenCCBridge.swift index f63a2726..43bc6a35 100644 --- a/Source/OpenCCBridge.swift +++ b/Source/OpenCCBridge.swift @@ -3,7 +3,7 @@ import OpenCC // Since SwiftyOpenCC only provide Swift classes, we create an NSObject subclass // in Swift in order to bridge the Swift classes into our Objective-C++ project. -class OpenCCBridge: NSObject { +public class OpenCCBridge: NSObject { private static let shared = OpenCCBridge() private var converter: ChineseConverter? @@ -12,11 +12,7 @@ class OpenCCBridge: NSObject { super.init() } - @objc static func convert(_ string: String) -> String? { + @objc public static func convert(_ string: String) -> String? { shared.converter?.convert(string) } - - private func convert(_ string: String) -> String? { - converter?.convert(string) - } } diff --git a/Source/main.m b/Source/main.m index a98863b0..f65d13a7 100644 --- a/Source/main.m +++ b/Source/main.m @@ -34,7 +34,7 @@ #import #import -#import "McBopomofo-Swift.h" +@import InputSourceHelper; static NSString *const kConnectionName = @"McBopomofo_1_Connection"; From fcdd59dd6bc2bbccb64cb64abe8b44a704633537 Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 00:57:41 +0800 Subject: [PATCH 08/11] Wraps OpenCCBridge into a SPM package. --- McBopomofo.xcodeproj/project.pbxproj | 29 +++++-------------- Packages/OpenCCBridge/.gitignore | 7 +++++ Packages/OpenCCBridge/Package.resolved | 16 ++++++++++ Packages/OpenCCBridge/Package.swift | 29 +++++++++++++++++++ Packages/OpenCCBridge/README.md | 3 ++ .../Sources/OpenCCBridge}/OpenCCBridge.swift | 0 .../OpenCCBridgeTests/OpenCCBridgeTests.swift | 11 +++++++ Source/InputMethodController.mm | 1 + 8 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 Packages/OpenCCBridge/.gitignore create mode 100644 Packages/OpenCCBridge/Package.resolved create mode 100644 Packages/OpenCCBridge/Package.swift create mode 100644 Packages/OpenCCBridge/README.md rename {Source => Packages/OpenCCBridge/Sources/OpenCCBridge}/OpenCCBridge.swift (100%) create mode 100644 Packages/OpenCCBridge/Tests/OpenCCBridgeTests/OpenCCBridgeTests.swift diff --git a/McBopomofo.xcodeproj/project.pbxproj b/McBopomofo.xcodeproj/project.pbxproj index af6e167a..6633cae7 100644 --- a/McBopomofo.xcodeproj/project.pbxproj +++ b/McBopomofo.xcodeproj/project.pbxproj @@ -45,8 +45,7 @@ D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7AD27907B8A004A2160 /* NotifierUI */; }; D427F7B4279086DC004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B3279086DC004A2160 /* InputSourceHelper */; }; D427F7B6279086F6004A2160 /* InputSourceHelper in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7B5279086F6004A2160 /* InputSourceHelper */; }; - D427F7BB27908D78004A2160 /* OpenCCBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = D427F7BA27908D78004A2160 /* OpenCCBridge.swift */; }; - D427F7BE27908DD7004A2160 /* OpenCC in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7BD27908DD7004A2160 /* OpenCC */; }; + D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */ = {isa = PBXBuildFile; productRef = D427F7C027908EFC004A2160 /* OpenCCBridge */; }; 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 */; }; @@ -172,7 +171,7 @@ D427F7A727905E43004A2160 /* TooltipUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TooltipUI; path = Packages/TooltipUI; sourceTree = ""; }; D427F7AC27907B7E004A2160 /* NotifierUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = NotifierUI; path = Packages/NotifierUI; sourceTree = ""; }; D427F7B2279086B5004A2160 /* InputSourceHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = InputSourceHelper; path = Packages/InputSourceHelper; sourceTree = ""; }; - D427F7BA27908D78004A2160 /* OpenCCBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenCCBridge.swift; sourceTree = ""; }; + D427F7BF27908EAC004A2160 /* OpenCCBridge */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = OpenCCBridge; path = Packages/OpenCCBridge; 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 = ""; }; @@ -185,11 +184,11 @@ buildActionMask = 2147483647; files = ( D427F7B4279086DC004A2160 /* InputSourceHelper in Frameworks */, + D427F7C127908EFC004A2160 /* OpenCCBridge in Frameworks */, 6A38BC2815FC158A00A8A51F /* InputMethodKit.framework in Frameworks */, D427F7A927905E90004A2160 /* TooltipUI in Frameworks */, D427F76A278C9E29004A2160 /* CandidateUI in Frameworks */, D427F7AE27907B8A004A2160 /* NotifierUI in Frameworks */, - D427F7BE27908DD7004A2160 /* OpenCC in Frameworks */, 6A0D4EA715FC0D2D00ABF4B3 /* Cocoa.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -251,7 +250,6 @@ D427F76B278CA1BA004A2160 /* AppDelegate.swift */, D47F7DCF278C0897002F9DD7 /* NonModalAlertWindowController.swift */, D47F7DCD278BFB57002F9DD7 /* PreferencesWindowController.swift */, - D427F7BA27908D78004A2160 /* OpenCCBridge.swift */, 6A0D4EF615FC0DA600ABF4B3 /* McBopomofo-Prefix.pch */, D427A9BF25ED28CC005D43E0 /* McBopomofo-Bridging-Header.h */, ); @@ -403,6 +401,7 @@ D427F7A727905E43004A2160 /* TooltipUI */, D427F7AC27907B7E004A2160 /* NotifierUI */, D427F7B2279086B5004A2160 /* InputSourceHelper */, + D427F7BF27908EAC004A2160 /* OpenCCBridge */, ); name = Packages; sourceTree = ""; @@ -446,7 +445,7 @@ D427F7A827905E90004A2160 /* TooltipUI */, D427F7AD27907B8A004A2160 /* NotifierUI */, D427F7B3279086DC004A2160 /* InputSourceHelper */, - D427F7BD27908DD7004A2160 /* OpenCC */, + D427F7C027908EFC004A2160 /* OpenCCBridge */, ); productName = McBopomofo; productReference = 6A0D4EA215FC0D2D00ABF4B3 /* McBopomofo.app */; @@ -498,7 +497,6 @@ ); mainGroup = 6A0D4E9215FC0CFA00ABF4B3; packageReferences = ( - D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */, ); productRefGroup = 6A0D4EA315FC0D2D00ABF4B3 /* Products */; projectDirPath = ""; @@ -578,7 +576,6 @@ D47F7DCE278BFB57002F9DD7 /* PreferencesWindowController.swift in Sources */, 6A0D4ED215FC0D6400ABF4B3 /* InputMethodController.mm in Sources */, D41355DB278E6D17005E5CBD /* McBopomofoLM.cpp in Sources */, - D427F7BB27908D78004A2160 /* OpenCCBridge.swift in Sources */, D47F7DD3278C1263002F9DD7 /* UserOverrideModel.cpp in Sources */, 6A0D4F4515FC0EB100ABF4B3 /* Mandarin.cpp in Sources */, D41355DE278EA3ED005E5CBD /* UserPhrasesLM.cpp in Sources */, @@ -1057,17 +1054,6 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ddddxxx/SwiftyOpenCC.git"; - requirement = { - kind = revision; - revision = a802c02cbf1c6fcd529575f11a9876d94fc813f4; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ D427F769278C9E29004A2160 /* CandidateUI */ = { isa = XCSwiftPackageProductDependency; @@ -1089,10 +1075,9 @@ isa = XCSwiftPackageProductDependency; productName = InputSourceHelper; }; - D427F7BD27908DD7004A2160 /* OpenCC */ = { + D427F7C027908EFC004A2160 /* OpenCCBridge */ = { isa = XCSwiftPackageProductDependency; - package = D427F7BC27908DD7004A2160 /* XCRemoteSwiftPackageReference "SwiftyOpenCC" */; - productName = OpenCC; + productName = OpenCCBridge; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Packages/OpenCCBridge/.gitignore b/Packages/OpenCCBridge/.gitignore new file mode 100644 index 00000000..bb460e7b --- /dev/null +++ b/Packages/OpenCCBridge/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/OpenCCBridge/Package.resolved b/Packages/OpenCCBridge/Package.resolved new file mode 100644 index 00000000..f306e3fb --- /dev/null +++ b/Packages/OpenCCBridge/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftyOpenCC", + "repositoryURL": "https://github.com/ddddxxx/SwiftyOpenCC.git", + "state": { + "branch": null, + "revision": "a802c02cbf1c6fcd529575f11a9876d94fc813f4", + "version": null + } + } + ] + }, + "version": 1 +} diff --git a/Packages/OpenCCBridge/Package.swift b/Packages/OpenCCBridge/Package.swift new file mode 100644 index 00000000..4c178660 --- /dev/null +++ b/Packages/OpenCCBridge/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version:5.5 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "OpenCCBridge", + products: [ + // Products define the executables and libraries a package produces, and make them visible to other packages. + .library( + name: "OpenCCBridge", + targets: ["OpenCCBridge"]), + ], + dependencies: [ + // Dependencies declare other packages that this package depends on. + // .package(url: /* package url */, from: "1.0.0"), + .package(name: "SwiftyOpenCC", url: "https://github.com/ddddxxx/SwiftyOpenCC.git", revision: "a802c02cbf1c6fcd529575f11a9876d94fc813f4") + + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .target( + name: "OpenCCBridge", + dependencies: [ + .product(name: "OpenCC", package: "SwiftyOpenCC") + ]), + ] +) diff --git a/Packages/OpenCCBridge/README.md b/Packages/OpenCCBridge/README.md new file mode 100644 index 00000000..2a347ed0 --- /dev/null +++ b/Packages/OpenCCBridge/README.md @@ -0,0 +1,3 @@ +# OpenCCBridge + +A description of this package. diff --git a/Source/OpenCCBridge.swift b/Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift similarity index 100% rename from Source/OpenCCBridge.swift rename to Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift diff --git a/Packages/OpenCCBridge/Tests/OpenCCBridgeTests/OpenCCBridgeTests.swift b/Packages/OpenCCBridge/Tests/OpenCCBridgeTests/OpenCCBridgeTests.swift new file mode 100644 index 00000000..2cda116c --- /dev/null +++ b/Packages/OpenCCBridge/Tests/OpenCCBridgeTests/OpenCCBridgeTests.swift @@ -0,0 +1,11 @@ +import XCTest +@testable import OpenCCBridge + +final class OpenCCBridgeTests: XCTestCase { + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct + // results. + XCTAssertEqual(OpenCCBridge().text, "Hello, World!") + } +} diff --git a/Source/InputMethodController.mm b/Source/InputMethodController.mm index 9d4cfb11..50140750 100644 --- a/Source/InputMethodController.mm +++ b/Source/InputMethodController.mm @@ -44,6 +44,7 @@ @import CandidateUI; @import NotifierUI; @import TooltipUI; +@import OpenCCBridge; // C++ namespace usages using namespace std; From 536aff10704059c941101a7bd3d799dd253c561d Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 01:38:32 +0800 Subject: [PATCH 09/11] Removes unused files. --- Packages/NonModalAlertWindow/.gitignore | 7 - Packages/NonModalAlertWindow/Package.swift | 25 --- Packages/NonModalAlertWindow/README.md | 3 - .../NonModalAlertWindowController.swift | 147 ------------------ .../NonModalAlertWindowController.xib | 85 ---------- 5 files changed, 267 deletions(-) delete mode 100644 Packages/NonModalAlertWindow/.gitignore delete mode 100644 Packages/NonModalAlertWindow/Package.swift delete mode 100644 Packages/NonModalAlertWindow/README.md delete mode 100644 Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift delete mode 100644 Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib diff --git a/Packages/NonModalAlertWindow/.gitignore b/Packages/NonModalAlertWindow/.gitignore deleted file mode 100644 index bb460e7b..00000000 --- a/Packages/NonModalAlertWindow/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.DS_Store -/.build -/Packages -/*.xcodeproj -xcuserdata/ -DerivedData/ -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata diff --git a/Packages/NonModalAlertWindow/Package.swift b/Packages/NonModalAlertWindow/Package.swift deleted file mode 100644 index 7e9a84ed..00000000 --- a/Packages/NonModalAlertWindow/Package.swift +++ /dev/null @@ -1,25 +0,0 @@ -// swift-tools-version:5.5 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "NonModalAlertWindow", - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "NonModalAlertWindow", - targets: ["NonModalAlertWindow"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "NonModalAlertWindow", - dependencies: []), - ] -) diff --git a/Packages/NonModalAlertWindow/README.md b/Packages/NonModalAlertWindow/README.md deleted file mode 100644 index 93b2b884..00000000 --- a/Packages/NonModalAlertWindow/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# NonModalAlertWindow - -A description of this package. diff --git a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift deleted file mode 100644 index 936250e9..00000000 --- a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/NonModalAlertWindowController.swift +++ /dev/null @@ -1,147 +0,0 @@ -// -// NonModalAlertWindowController.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 - -@objc public protocol NonModalAlertWindowControllerDelegate: AnyObject { - func nonModalAlertWindowControllerDidConfirm(_ controller: NonModalAlertWindowController) - func nonModalAlertWindowControllerDidCancel(_ controller: NonModalAlertWindowController) -} - -@objc public class NonModalAlertWindowController: NSWindowController { - @objc (sharedInstance) - public static let shared: NonModalAlertWindowController = { - NSLog("shaerd???") - guard let path = Bundle.main.path(forResource: "NonModalAlertWindow_NonModalAlertWindow", ofType: "bundle"), - let modelBudle = Bundle(path: path), - let xibPath = modelBudle.path(forResource: "NonModalAlertWindowController", ofType: "nib") - else { - let controller = NonModalAlertWindowController(windowNibName: "NonModalAlertWindowController") - return controller - } - NSLog("shared path \(path)") - NSLog("shared modelBudle \(modelBudle)") - NSLog("shared xibPath \(xibPath)") - let controller = NonModalAlertWindowController(windowNibPath: xibPath, owner: self) - return controller - }() - - @IBOutlet weak var titleTextField: NSTextField! - @IBOutlet weak var contentTextField: NSTextField! - @IBOutlet weak var confirmButton: NSButton! - @IBOutlet weak var cancelButton: NSButton! - weak var delegate: NonModalAlertWindowControllerDelegate? - - @objc public func show(title: String, content: String, confirmButtonTitle: String, cancelButtonTitle: String?, cancelAsDefault: Bool, delegate: NonModalAlertWindowControllerDelegate?) { - if window?.isVisible == true { - self.delegate?.nonModalAlertWindowControllerDidCancel(self) - } - - self.delegate = delegate - - var oldFrame = confirmButton.frame - confirmButton.title = confirmButtonTitle - confirmButton.sizeToFit() - - var newFrame = confirmButton.frame - newFrame.size.width = max(90, newFrame.size.width + 10) - newFrame.origin.x += oldFrame.size.width - newFrame.size.width - confirmButton.frame = newFrame - - if let cancelButtonTitle = cancelButtonTitle { - cancelButton.title = cancelButtonTitle - cancelButton.sizeToFit() - var adjustFrame = cancelButton.frame - adjustFrame.size.width = max(90, adjustFrame.size.width + 10) - adjustFrame.origin.x = newFrame.origin.x - adjustFrame.size.width - confirmButton.frame = adjustFrame - cancelButton.isHidden = false - } else { - cancelButton.isHidden = true - } - - cancelButton.nextKeyView = confirmButton - confirmButton.nextKeyView = cancelButton - - if cancelButtonTitle != nil { - if cancelAsDefault { - window?.defaultButtonCell = cancelButton.cell as? NSButtonCell - } else { - cancelButton.keyEquivalent = " " - window?.defaultButtonCell = confirmButton.cell as? NSButtonCell - } - } else { - window?.defaultButtonCell = confirmButton.cell as? NSButtonCell - } - - titleTextField.stringValue = title - - oldFrame = contentTextField.frame - contentTextField.stringValue = content - - var infiniteHeightFrame = oldFrame - infiniteHeightFrame.size.width -= 4.0 - infiniteHeightFrame.size.height = 10240 - newFrame = (content as NSString).boundingRect(with: infiniteHeightFrame.size, options: [.usesLineFragmentOrigin], attributes: [.font: contentTextField.font!]) - newFrame.size.width = max(newFrame.size.width, oldFrame.size.width) - newFrame.size.height += 4.0 - newFrame.origin = oldFrame.origin - newFrame.origin.y -= (newFrame.size.height - oldFrame.size.height) - contentTextField.frame = newFrame - - var windowFrame = window?.frame ?? NSRect.zero - windowFrame.size.height += (newFrame.size.height - oldFrame.size.height) - window?.level = NSWindow.Level(Int(CGShieldingWindowLevel()) + 1) - window?.setFrame(windowFrame, display: true) - window?.center() - window?.makeKeyAndOrderFront(self) - NSApp.activate(ignoringOtherApps: true) - } - - @IBAction func confirmButtonAction(_ sender: Any) { - delegate?.nonModalAlertWindowControllerDidConfirm(self) - window?.orderOut(self) - } - - @IBAction func cancelButtonAction(_ sender: Any) { - cancel(sender) - } - - @objc public func cancel(_ sender: Any) { - delegate?.nonModalAlertWindowControllerDidCancel(self) - delegate = nil - window?.orderOut(self) - } - -} diff --git a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib b/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib deleted file mode 100644 index 8c378007..00000000 --- a/Packages/NonModalAlertWindow/Sources/NonModalAlertWindow/Resources/NonModalAlertWindowController.xib +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 34e193df2188953c913aed180ebec866d6543f8d Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 02:34:23 +0800 Subject: [PATCH 10/11] Also updates the README file. --- README.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index f0fa1229..93497acb 100644 --- a/README.markdown +++ b/README.markdown @@ -2,6 +2,8 @@ ## 系統需求 +小麥注音輸入法可以在 macOS 10.10 以上版本運作。如果您要自行編譯小麥注音輸入法,或參與開發,您需要: + - macOS 10.15 Catalina 以上版本 - Xcode 12.0 以上版本 @@ -18,4 +20,3 @@ ## 軟體授權 本專案採用 MIT License 釋出,使用者可自由使用、散播本軟體,惟散播時必須完整保留版權聲明及軟體授權([詳全文](https://github.com/openvanilla/McBopomofo/blob/master/LICENSE.txt))。 - From 3ca0eddd2389ca58bc9f5457927d351e18fed9ac Mon Sep 17 00:00:00 2001 From: zonble Date: Fri, 14 Jan 2022 02:48:21 +0800 Subject: [PATCH 11/11] Makes some members private. --- .../NotifierUI/NotifierController.swift | 68 +++++++++---------- .../Sources/OpenCCBridge/OpenCCBridge.swift | 2 +- .../Sources/TooltipUI/TooltipController.swift | 6 +- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift b/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift index ae1bedc9..b940f4f9 100644 --- a/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift +++ b/Packages/NotifierUI/Sources/NotifierUI/NotifierController.swift @@ -1,10 +1,10 @@ import Cocoa -protocol NotifierWindowDelegate: AnyObject { +private protocol NotifierWindowDelegate: AnyObject { func windowDidBecomeClicked(_ window: NotifierWindow) } -class NotifierWindow: NSWindow { +private class NotifierWindow: NSWindow { weak var clickDelegate: NotifierWindowDelegate? override func mouseDown(with event: NSEvent) { @@ -12,8 +12,8 @@ class NotifierWindow: NSWindow { } } -let kWindowWidth: CGFloat = 160.0 -let kWindowHeight: CGFloat = 80.0 +private let kWindowWidth: CGFloat = 160.0 +private let kWindowHeight: CGFloat = 80.0 public class NotifierController: NSWindowController, NotifierWindowDelegate { private var messageTextField: NSTextField @@ -64,18 +64,18 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { controller.show() } - static func increaseInstanceCount() { + private static func increaseInstanceCount() { instanceCount += 1 } - static func decreaseInstanceCount() { + private static func decreaseInstanceCount() { instanceCount -= 1 if instanceCount < 0 { instanceCount = 0 } } - public init() { + private init() { let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero let contentRect = NSRect(x: 0, y: 0, width: kWindowWidth, height: kWindowHeight) var windowRect = contentRect @@ -107,41 +107,41 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { fatalError("init(coder:) has not been implemented") } - func setStartLocation() { - if NotifierController.instanceCount == 0 { - return - } - let lastLocation = NotifierController.lastLocation - let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero - var windowRect = self.window?.frame ?? NSRect.zero - windowRect.origin.x = lastLocation.x - windowRect.origin.y = lastLocation.y - 10 - windowRect.height + private func show() { + func setStartLocation() { + if NotifierController.instanceCount == 0 { + return + } + let lastLocation = NotifierController.lastLocation + let screenRect = NSScreen.main?.visibleFrame ?? NSRect.zero + var windowRect = self.window?.frame ?? NSRect.zero + windowRect.origin.x = lastLocation.x + windowRect.origin.y = lastLocation.y - 10 - windowRect.height - if windowRect.origin.y < screenRect.minY { - return + if windowRect.origin.y < screenRect.minY { + return + } + + self.window?.setFrame(windowRect, display: true) } - self.window?.setFrame(windowRect, display: true) - } + func moveIn() { + let afterRect = self.window?.frame ?? NSRect.zero + NotifierController.lastLocation = afterRect.origin + var beforeRect = afterRect + beforeRect.origin.y += 10 + window?.setFrame(beforeRect, display: true) + window?.orderFront(self) + window?.setFrame(afterRect, display: true, animate: true) + } - func moveIn() { - let afterRect = self.window?.frame ?? NSRect.zero - NotifierController.lastLocation = afterRect.origin - var beforeRect = afterRect - beforeRect.origin.y += 10 - window?.setFrame(beforeRect, display: true) - window?.orderFront(self) - window?.setFrame(afterRect, display: true, animate: true) - } - - func show() { setStartLocation() moveIn() NotifierController.increaseInstanceCount() waitTimer = Timer.scheduledTimer(timeInterval: shouldStay ? 5 : 1, target: self, selector: #selector(fadeOut), userInfo: nil, repeats: false) } - @objc func doFadeOut(_ timer: Timer) { + @objc private func doFadeOut(_ timer: Timer) { let opacity = self.window?.alphaValue ?? 0 if opacity <= 0 { self.close() @@ -150,7 +150,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { } } - @objc func fadeOut() { + @objc private func fadeOut() { waitTimer?.invalidate() waitTimer = nil NotifierController.decreaseInstanceCount() @@ -165,7 +165,7 @@ public class NotifierController: NSWindowController, NotifierWindowDelegate { super.close() } - func windowDidBecomeClicked(_ window: NotifierWindow) { + fileprivate func windowDidBecomeClicked(_ window: NotifierWindow) { self.fadeOut() } } diff --git a/Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift b/Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift index 43bc6a35..4999a686 100644 --- a/Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift +++ b/Packages/OpenCCBridge/Sources/OpenCCBridge/OpenCCBridge.swift @@ -7,7 +7,7 @@ public class OpenCCBridge: NSObject { private static let shared = OpenCCBridge() private var converter: ChineseConverter? - override init() { + private override init() { try? converter = ChineseConverter(options: .simplify) super.init() } diff --git a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift index 0103ec12..dbbbeb78 100644 --- a/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift +++ b/Packages/TooltipUI/Sources/TooltipUI/TooltipController.swift @@ -1,9 +1,9 @@ import Cocoa public class TooltipController: NSWindowController { - let backgroundColor = NSColor(calibratedHue: 0.16, saturation: 0.22, brightness: 0.97, alpha: 1.0) - var messageTextField: NSTextField - var tooltip: String = "" { + private let backgroundColor = NSColor(calibratedHue: 0.16, saturation: 0.22, brightness: 0.97, alpha: 1.0) + private var messageTextField: NSTextField + private var tooltip: String = "" { didSet { messageTextField.stringValue = tooltip adjustSize()