From 7796c3ecd57c50b9f960ba868dd9b1192a267eff Mon Sep 17 00:00:00 2001 From: groverlynn Date: Tue, 14 Nov 2023 04:05:38 +0100 Subject: [PATCH] compatible with predictor && cleanup codes --- Info.plist | 27 +- Sparkle | 2 +- SquirrelApplicationDelegate.m | 14 +- SquirrelConfig.m | 4 +- SquirrelInputController.h | 22 +- SquirrelInputController.m | 287 ++++++++++---------- SquirrelPanel.h | 12 +- SquirrelPanel.m | 482 ++++++++++++++++------------------ input_source.m | 10 +- librime | 2 +- macos_keycode.m | 7 +- main.m | 6 +- 12 files changed, 445 insertions(+), 430 deletions(-) diff --git a/Info.plist b/Info.plist index f09819368..89c110a22 100644 --- a/Info.plist +++ b/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion English CFBundleExecutable - Squirrel + ${EXECUTABLE_NAME} CFBundleIconFile RimeIcon.icns CFBundleIdentifier @@ -13,13 +13,15 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Squirrel + ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion $(CURRENT_PROJECT_VERSION) + CFBundleShortVersionString + ${MARKETING_VERSION} ComponentInputModeDict tsInputModeListKey @@ -27,15 +29,14 @@ im.rime.inputmethod.Squirrel.Hant TISInputSourceID - im.rime.inputmethod.Squirrel.Hant + ${PRODUCT_BUNDLE_IDENTIFIER}.Hant TISIntendedLanguage zh-Hant TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hant - Hans + Hani tsInputModeDefaultStateKey @@ -55,15 +56,14 @@ im.rime.inputmethod.Squirrel.Hans TISInputSourceID - im.rime.inputmethod.Squirrel.Hans + ${PRODUCT_BUNDLE_IDENTIFIER}.Hans TISIntendedLanguage zh-Hans TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hans - Hant + Hani tsInputModeDefaultStateKey @@ -83,15 +83,14 @@ im.rime.inputmethod.Squirrel.Cant TISInputSourceID - im.rime.inputmethod.Squirrel.Cant + ${PRODUCT_BUNDLE_IDENTIFIER}.Cant TISIntendedLanguage yue-Hant TISIconIsTemplate tsInputModeCharacterRepertoireKey - Hant - Hans + Hani tsInputModeDefaultStateKey @@ -117,7 +116,7 @@ InputMethodConnectionName - Squirrel_1_Connection + ${PRODUCT_NAME}_1_Connection InputMethodServerControllerClass SquirrelInputController InputMethodServerDelegateClass @@ -142,6 +141,10 @@ TISIconIsTemplate + tsInputMethodCharacterRepertoireKey + + Hani + tsInputMethodIconFileKey rime tsInputMethodAlternateMenuIconFileKey diff --git a/Sparkle b/Sparkle index 1f07f4096..8fb9c83ad 160000 --- a/Sparkle +++ b/Sparkle @@ -1 +1 @@ -Subproject commit 1f07f4096e52f19b5e7abaa697b7fc592b7ff57c +Subproject commit 8fb9c83adf6f74364ee57bf314ceee2fa77d0ac2 diff --git a/SquirrelApplicationDelegate.m b/SquirrelApplicationDelegate.m index 82cc54133..ac49c4266 100644 --- a/SquirrelApplicationDelegate.m +++ b/SquirrelApplicationDelegate.m @@ -4,7 +4,7 @@ #import "SquirrelConfig.h" #import "SquirrelPanel.h" -static NSString *const kRimeWikiURL = @"https://github.com/rime/home/wiki"; +static NSString *kRimeWikiURL = @"https://github.com/rime/home/wiki"; @implementation SquirrelApplicationDelegate @@ -148,8 +148,8 @@ - (void)loadSettings { _enableNotifications = ![[_config getString:@"show_notifications_when"] isEqualToString:@"never"]; - [self.panel loadConfig:_config forDarkMode:NO]; - [self.panel loadConfig:_config forDarkMode:YES]; + [self.panel loadConfig:_config forAppearance:defaultAppear]; + [self.panel loadConfig:_config forAppearance:darkAppear]; } - (void)loadSchemaSpecificSettings:(NSString *)schemaId { @@ -159,11 +159,11 @@ - (void)loadSchemaSpecificSettings:(NSString *)schemaId { SquirrelConfig *schema = [[SquirrelConfig alloc] init]; if ([schema openWithSchemaId:schemaId baseConfig:self.config] && [schema hasSection:@"style"]) { - [self.panel loadConfig:schema forDarkMode:NO]; - [self.panel loadConfig:schema forDarkMode:YES]; + [self.panel loadConfig:schema forAppearance:defaultAppear]; + [self.panel loadConfig:schema forAppearance:darkAppear]; } else { - [self.panel loadConfig:self.config forDarkMode:NO]; - [self.panel loadConfig:self.config forDarkMode:YES]; + [self.panel loadConfig:self.config forAppearance:defaultAppear]; + [self.panel loadConfig:self.config forAppearance:darkAppear]; } [schema close]; } diff --git a/SquirrelConfig.m b/SquirrelConfig.m index e365f6cbd..f35479a34 100644 --- a/SquirrelConfig.m +++ b/SquirrelConfig.m @@ -274,8 +274,8 @@ - (SquirrelOptionSwitcher *)getOptionSwitcher { } RimeConfigEnd(&optionIter); if (hasStyleSection) { - for (NSUInteger i = 0; i < optionGroup.count; ++i) { - switcher[optionGroup[i]] = optionGroup[reset]; + for (size_t i = 0; i < optionGroup.count; ++i) { + switcher[optionGroup[i]] = optionGroup[(size_t)reset]; optionGroups[optionGroup[i]] = optionGroup; } } diff --git a/SquirrelInputController.h b/SquirrelInputController.h index 92d8c0197..0177b1a28 100644 --- a/SquirrelInputController.h +++ b/SquirrelInputController.h @@ -3,10 +3,22 @@ @interface SquirrelInputController : IMKInputController -- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index; +typedef enum { + kSELECT = 1, // accepts indices in both digits and selection keys + kDELETE = 2, // only accepts indices in digits, e.g. (int) 1 + kCHOOSE = 3 // only accepts indices in selection keys, e.g. (char) '1' / 'A' +} rimeAction; -@end +typedef enum { + // 0 ... 9 are ordinal digits, used as (int) index + // 0x21 ... 0x7e are ASCII chars (as selection keys) + // other rime keycodes (as function keys), for paging etc. + kPageUp = 0xff55, // XK_Page_Up + kPageDown = 0xff56, // XK_Page_Down + kVoidSymbol = 0xffffff // XK_VoidSymbol +} rimeIndex; + +- (void)perform:(rimeAction)action + onIndex:(rimeIndex)index; -#define kSELECT 0x1 -#define kDELETE 0x2 -#define kCHOOSE 0x3 +@end diff --git a/SquirrelInputController.m b/SquirrelInputController.m index 1cf6f5d94..275a00599 100644 --- a/SquirrelInputController.m +++ b/SquirrelInputController.m @@ -10,7 +10,7 @@ @interface SquirrelInputController (Private) - (void)createSession; - (void)destroySession; -- (BOOL)rimeConsumeCommittedText; +- (void)rimeConsumeCommittedText; - (void)updateStyleOptions; - (void)rimeUpdate; - (void)updateAppOptions; @@ -20,17 +20,18 @@ - (void)updateAppOptions; @implementation SquirrelInputController { NSMutableAttributedString *_preeditString; - NSAttributedString *_originalString; + NSString *_originalString; NSString *_composedString; NSRange _selRange; NSUInteger _caretPos; NSArray *_candidates; - NSUInteger _lastModifier; + NSEventModifierFlags _lastModifier; uint _lastEventCount; RimeSessionId _session; NSString *_schemaId; BOOL _inlinePreedit; BOOL _inlineCandidate; + BOOL _showingSwitcherMenu; // app-specific bug fix BOOL _inlinePlaceHolder; BOOL _panellessCommitFix; @@ -48,31 +49,18 @@ @implementation SquirrelInputController { @abstract Receive incoming event @discussion This method receives key events from the client application. */ -- (BOOL)handleEvent:(NSEvent *)event client:(id)sender +- (BOOL)handleEvent:(NSEvent *)event + client:(id)sender { // Return YES to indicate the the key input was received and dealt with. // Key processing will not continue in that case. In other words the // system will not deliver a key down event to the application. // Returning NO means the original key down will be passed on to the client. - NSUInteger modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; + NSEventModifierFlags modifiers = event.modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask; BOOL handled = NO; @autoreleasepool { - if (!_session || !RimeFindSession(_session)) { - [self createSession]; - if (!_session) { - return NO; - } - } - - NSString *app = [sender bundleIdentifier]; - - if (![_currentApp isEqualToString:app]) { - _currentApp = [app copy]; - [self updateAppOptions]; - } - switch (event.type) { case NSEventTypeFlagsChanged: { if (_lastModifier == modifiers) { @@ -83,7 +71,7 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender //NSLog(@"FLAGSCHANGED client: %@, modifiers: 0x%lx", sender, modifiers); int release_mask = 0; int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); - ushort keyCode = CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); + ushort keyCode = (ushort)CGEventGetIntegerValueField(event.CGEvent, kCGKeyboardEventKeycode); int rime_keycode = osx_keycode_to_rime_keycode(keyCode, 0, 0, 0); uint eventCount = CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventFlagsChanged) + CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventKeyDown) + @@ -92,36 +80,34 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender CGEventSourceCounterForEventType(kCGEventSourceStateCombinedSessionState, kCGEventOtherMouseDown); _lastModifier = modifiers; switch (keyCode) { - case kVK_CapsLock: { + case kVK_CapsLock: rime_modifiers ^= kLockMask; [self processKey:rime_keycode modifiers:rime_modifiers]; - } break; + break; case kVK_Shift: - case kVK_RightShift: { - release_mask = modifiers & OSX_SHIFT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightShift: + release_mask = modifiers & NSEventModifierFlagShift ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Control: - case kVK_RightControl: { - release_mask = modifiers & OSX_CTRL_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightControl: + release_mask = modifiers & NSEventModifierFlagControl ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Option: - case kVK_RightOption: { - release_mask = modifiers & OSX_ALT_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightOption: + release_mask = modifiers & NSEventModifierFlagOption ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; - case kVK_Function: { - release_mask = modifiers & OSX_FN_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + break; + case kVK_Function: + release_mask = modifiers & NSEventModifierFlagFunction ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; - } break; + break; case kVK_Command: - case kVK_RightCommand: { - release_mask = modifiers & OSX_COMMAND_MASK ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); + case kVK_RightCommand: + release_mask = modifiers & NSEventModifierFlagCommand ? 0 : kReleaseMask | (eventCount - _lastEventCount == 1 ? 0 : kIgnoredMask); [self processKey:rime_keycode modifiers:(rime_modifiers | release_mask)]; goto saveStatus; - } - default: break; } [self rimeUpdate]; @@ -129,20 +115,20 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender } break; case NSEventTypeKeyDown: { // ignore Command+X hotkeys. - if (modifiers & OSX_COMMAND_MASK) { + if (modifiers & NSEventModifierFlagCommand) { break; } ushort keyCode = event.keyCode; - NSString *keyChars = ((modifiers & OSX_SHIFT_MASK) && !(modifiers & OSX_CTRL_MASK) && - !(modifiers & OSX_ALT_MASK)) ? event.characters : event.charactersIgnoringModifiers; + NSString *keyChars = ((modifiers & NSEventModifierFlagShift) && !(modifiers & NSEventModifierFlagControl) && + !(modifiers & NSEventModifierFlagOption)) ? event.characters : event.charactersIgnoringModifiers; //NSLog(@"KEYDOWN client: %@, modifiers: 0x%lx, keyCode: %d, keyChars: [%@]", // sender, modifiers, keyCode, keyChars); // translate osx keyevents to rime keyevents int rime_keycode = osx_keycode_to_rime_keycode(keyCode, [keyChars characterAtIndex:0], - modifiers & OSX_SHIFT_MASK, - modifiers & OSX_CAPITAL_MASK); + modifiers & NSEventModifierFlagShift, + modifiers & NSEventModifierFlagCapsLock); if (rime_keycode) { int rime_modifiers = osx_modifiers_to_rime_modifiers(modifiers); // revert non-modifier function keys' FunctionKeyMask (FwdDel, Navigations, F1..F19) @@ -161,7 +147,8 @@ - (BOOL)handleEvent:(NSEvent *)event client:(id)sender return handled; } -- (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers +- (BOOL)processKey:(int)rime_keycode + modifiers:(int)rime_modifiers { // with linear candidate list, arrow keys may behave differently. Bool is_linear = NSApp.squirrelAppDelegate.panel.linear; @@ -183,8 +170,7 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers BOOL isVimBackInCommandMode = rime_keycode == XK_Escape || ((rime_modifiers & kControlMask) && (rime_keycode == XK_c || rime_keycode == XK_C || rime_keycode == XK_bracketleft)); - if (isVimBackInCommandMode && - RimeGetOption(_session, "vim_mode") && + if (isVimBackInCommandMode && RimeGetOption(_session, "vim_mode") && !RimeGetOption(_session, "ascii_mode")) { RimeSetOption(_session, "ascii_mode", True); // NSLog(@"turned Chinese mode off in vim-like editor's command mode"); @@ -195,11 +181,10 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers if (handled) { BOOL is_chording_key = (rime_keycode >= XK_space && rime_keycode <= XK_asciitilde) || - rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || - rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || - rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; - if (is_chording_key && - RimeGetOption(_session, "_chord_typing")) { + rime_keycode == XK_Control_L || rime_keycode == XK_Control_R || + rime_keycode == XK_Alt_L || rime_keycode == XK_Alt_R || + rime_keycode == XK_Shift_L || rime_keycode == XK_Shift_R; + if (is_chording_key && RimeGetOption(_session, "_chord_typing")) { [self updateChord:rime_keycode modifiers:rime_modifiers]; } else if ((rime_modifiers & kReleaseMask) == 0) { // non-chording key pressed @@ -210,25 +195,21 @@ - (BOOL)processKey:(int)rime_keycode modifiers:(int)rime_modifiers return handled; } -- (BOOL)perform:(NSUInteger)action onIndex:(NSUInteger)index { - BOOL handled = NO; - if (index == NSPageUpFunctionKey && action == kSELECT) { - handled = RimeProcessKey(_session, XK_Page_Up, 0); - } else if (index == NSPageDownFunctionKey && action == kSELECT) { - handled = RimeProcessKey(_session, XK_Page_Down, 0); - } else if (index >= 0 && index < _candidates.count) { - if (action == kSELECT) { - handled = RimeSelectCandidateOnCurrentPage(_session, index); - } else if (action == kCHOOSE) { - handled = RimeChooseCandidateOnCurrentPage(_session, index); - } else if (action == kDELETE) { - handled = RimeDeleteCandidateOnCurrentPage(_session, index); - } +- (void)perform:(rimeAction)action + onIndex:(rimeIndex)index { + bool handled = false; + if (action == kSELECT && ((index >= '!' && index <= '~') || + index == kPageUp || index == kPageDown)) { + handled = RimeProcessKey(_session, (int)index, 0); + } else if (action == kCHOOSE && index >= '!' && index <= '~') { + handled = RimeProcessKey(_session, (int)index, kAltMask); + } else if (action == kDELETE && index >= 0 && index < 10) { + // kDELETE takes ordinal digits (instead of characters) as indexes + handled= RimeDeleteCandidateOnCurrentPage(_session, (size_t)index); } - if (handled && action != kCHOOSE) { + if (handled) { [self rimeUpdate]; } - return handled; } - (void)onChordTimer:(NSTimer *)timer @@ -250,7 +231,8 @@ - (void)onChordTimer:(NSTimer *)timer } } -- (void)updateChord:(int)keycode modifiers:(int)modifiers +- (void)updateChord:(int)keycode + modifiers:(int)modifiers { //NSLog(@"update chord: {%s} << %x", _chord, keycode); for (uint i = 0; i < _chordKeyCount; ++i) { @@ -312,16 +294,32 @@ - (void)activateServer:(id)sender if (keyboardLayout) { [sender overrideKeyboardWithKeyboardNamed:keyboardLayout]; } - _preeditString = nil; - _originalString = nil; - _composedString = nil; + + if (!_session || !RimeFindSession(_session)) { + [self createSession]; + } + NSString *app = [sender bundleIdentifier]; + if (![_currentApp isEqualToString:app]) { + _currentApp = [app copy]; + [self updateAppOptions]; + } + + SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; + panel.inputController = self; + panel.level = self.client.windowLevel + 1; + [super activateServer:sender]; } -- (instancetype)initWithServer:(IMKServer *)server delegate:(id)delegate client:(id)inputClient +- (instancetype)initWithServer:(IMKServer *)server + delegate:(id)delegate + client:(id)inputClient { //NSLog(@"initWithServer:delegate:client:"); if (self = [super initWithServer:server delegate:delegate client:inputClient]) { [self createSession]; + _composedString = [[NSString alloc] init]; + _originalString = [[NSString alloc] init]; + _preeditString = [[NSMutableAttributedString alloc] init]; } return self; } @@ -330,6 +328,7 @@ - (void)deactivateServer:(id)sender { //NSLog(@"deactivateServer:"); [self commitComposition:sender]; + [super deactivateServer:sender]; } /*! @@ -348,6 +347,7 @@ - (void)commitComposition:(id)sender //NSLog(@"commitComposition:"); if (_session) { [self commitString:[self composedString:sender]]; + [self hidePalettes]; RimeClearComposition(_session); } } @@ -388,7 +388,7 @@ - (NSMenu *)menu - (NSAttributedString *)originalString:(id)sender { - return _originalString; + return [[NSAttributedString alloc] initWithString:_originalString]; } - (id)composedString:(id)sender @@ -409,6 +409,9 @@ - (void)hidePalettes - (void)dealloc { [self destroySession]; + _originalString = nil; + _composedString = nil; + _preeditString = nil; } - (NSRange)selectionRange @@ -426,11 +429,10 @@ - (void)commitString:(id)string //NSLog(@"commitString:"); [self.client insertText:string replacementRange:self.replacementRange]; - [self hidePalettes]; - _composedString = nil; - _originalString = nil; - _preeditString = nil; + _composedString = @""; + _originalString = @""; + [_preeditString deleteCharactersInRange:NSMakeRange(0, _preeditString.length)]; } - (void)cancelComposition @@ -491,18 +493,29 @@ - (void)showPanelWithPreedit:(NSString *)preedit [self.client attributesForCharacterIndex:0 lineHeightRectangle:&inputPos]; SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel; if (@available(macOS 14.0, *)) { // avoid overlapping with cursor effects view - if (_lastModifier & OSX_CAPITAL_MASK) { - if (NSHeight(inputPos) > NSWidth(inputPos)) { - inputPos.size.height += 26; - inputPos.origin.y -= 26; - } else { - inputPos.size.width += 33; - inputPos.origin.x -= 33; + if (_lastModifier & NSEventModifierFlagCapsLock) { + NSRect screenRect = [[NSScreen mainScreen] frame]; + if (NSIntersectsRect(inputPos, screenRect)) { + screenRect = [[NSScreen mainScreen] visibleFrame]; + if (NSWidth(inputPos) > NSHeight(inputPos)) { + NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos) - 30, NSMinY(inputPos), 27, NSHeight(inputPos)); + if (NSMinX(capslockAccessory) < NSMinX(screenRect)) + capslockAccessory.origin.x = NSMinX(screenRect); + if (NSMaxX(capslockAccessory) > NSMaxX(screenRect)) + capslockAccessory.origin.x = NSMaxX(screenRect) - NSWidth(capslockAccessory); + inputPos = NSUnionRect(inputPos, capslockAccessory); + } else { + NSRect capslockAccessory = NSMakeRect(NSMinX(inputPos), NSMinY(inputPos) - 26, NSWidth(inputPos), 23); + if (NSMinY(capslockAccessory) < NSMinY(screenRect)) + capslockAccessory.origin.y = NSMaxY(screenRect) + 3; + if (NSMaxY(capslockAccessory) > NSMaxY(screenRect)) + capslockAccessory.origin.y = NSMaxY(screenRect) - NSHeight(capslockAccessory); + inputPos = NSUnionRect(inputPos, capslockAccessory); + } } } } panel.position = inputPos; - panel.inputController = self; [panel showPreedit:preedit selRange:selRange caretPos:caretPos @@ -510,9 +523,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit comments:comments highlighted:index pageNum:pageNum - lastPage:lastPage - turnPage:NSNotFound - update:YES]; + lastPage:lastPage]; } @end // SquirrelController @@ -543,7 +554,7 @@ - (void)updateAppOptions SquirrelAppOptions *appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:_currentApp]; if (appOptions) { for (NSString *key in appOptions) { - BOOL value = appOptions[key].boolValue; + Bool value = [appOptions[key] intValue]; NSLog(@"set app option: %@ = %d", key, value); RimeSetOption(_session, key.UTF8String, value); } @@ -562,7 +573,7 @@ - (void)destroySession [self clearChord]; } -- (BOOL)rimeConsumeCommittedText +- (void)rimeConsumeCommittedText { RIME_STRUCT(RimeCommit, commit); if (RimeGetCommit(_session, &commit)) { @@ -572,9 +583,7 @@ - (BOOL)rimeConsumeCommittedText } [self commitString:commitText]; RimeFreeCommit(&commit); - return YES; } - return NO; } - (void)updateStyleOptions @@ -614,45 +623,46 @@ - (void)updateStyleOptions - (void)rimeUpdate { //NSLog(@"rimeUpdate"); - if ([self rimeConsumeCommittedText]) { - return; - } - - BOOL switcherMenu = RimeGetOption(_session, "dumb"); + [self rimeConsumeCommittedText]; RIME_STRUCT(RimeStatus, status); if (RimeGetStatus(_session, &status)) { // enable schema specific ui style - if (!switcherMenu && (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { + if ((!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id))) { _schemaId = @(status.schema_id); - [self updateStyleOptions]; - [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; - [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; - // inline preedit - _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && - !RimeGetOption(_session, "no_inline")) || - RimeGetOption(_session, "inline"); - _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && - !RimeGetOption(_session, "no_inline")); - // if not inline, embed soft cursor in preedit string - RimeSetOption(_session, "soft_cursor", !_inlinePreedit); + if (!(_showingSwitcherMenu = RimeGetOption(_session, "dumb"))) { + [self updateStyleOptions]; + [NSApp.squirrelAppDelegate loadSchemaSpecificLabels:_schemaId]; + [NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId]; + // inline preedit + _inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit && + !RimeGetOption(_session, "no_inline")) || RimeGetOption(_session, "inline"); + _inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate && + !RimeGetOption(_session, "no_inline")); + // if not inline, embed soft cursor in preedit string + RimeSetOption(_session, "soft_cursor", !_inlinePreedit); + } } RimeFreeStatus(&status); } RIME_STRUCT(RimeContext, ctx); if (RimeGetContext(_session, &ctx)) { + // update raw input + const char *raw_input = RimeGetInput(_session); + _originalString = raw_input ? @(raw_input) : @""; + // update preedit text const char *preedit = ctx.composition.preedit; NSString *preeditText = preedit ? @(preedit) : @""; // update composed string - if (!preedit || switcherMenu) { + if (!preedit || _showingSwitcherMenu) { _composedString = @""; } else if (RimeGetOption(_session, "soft_cursor")) { - int cursorPos = ctx.composition.cursor_pos; + NSUInteger cursorPos = (NSUInteger)ctx.composition.cursor_pos; char composed[strlen(preedit) - 3]; - for (size_t i = 0; i < strlen(preedit) - 3; ++i) { + for (NSUInteger i = 0; i < strlen(preedit) - 3; ++i) { composed[i] = preedit[i < cursorPos ? i : i + 3]; } _composedString = [@(composed) stringByReplacingOccurrencesOfString:@" " withString:@""]; @@ -660,35 +670,35 @@ - (void)rimeUpdate _composedString = [@(preedit) stringByReplacingOccurrencesOfString:@" " withString:@""]; } - // update raw input - const char *raw_input = RimeGetInput(_session); - _originalString = [[NSAttributedString alloc] initWithString:@(raw_input)]; - - NSUInteger start = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; - NSUInteger end = [[NSString alloc] initWithBytes:preedit length:ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; - NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; - if (_inlineCandidate && !switcherMenu) { + NSUInteger start = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_start encoding:NSUTF8StringEncoding].length; + NSUInteger end = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.sel_end encoding:NSUTF8StringEncoding].length; + NSUInteger caretPos = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.cursor_pos encoding:NSUTF8StringEncoding].length; + NSUInteger length = [[NSString alloc] initWithBytes:preedit length:(NSUInteger)ctx.composition.length encoding:NSUTF8StringEncoding].length; + if (_inlineCandidate && !_showingSwitcherMenu) { const char *candidatePreview = ctx.commit_text_preview; NSString *candidatePreviewText = candidatePreview ? @(candidatePreview) : @""; if (_inlinePreedit) { - if ((caretPos >= end) && (caretPos < preeditText.length)) { - candidatePreviewText = [candidatePreviewText stringByAppendingString:[preeditText substringWithRange:NSMakeRange(caretPos, preeditText.length - caretPos)]]; + if ((caretPos >= end) && (caretPos < length)) { + candidatePreviewText = [candidatePreviewText stringByAppendingString: + [preeditText substringWithRange:NSMakeRange(caretPos, length - caretPos)]]; } [self showPreeditString:candidatePreviewText - selRange:NSMakeRange(start, candidatePreviewText.length - (preeditText.length - end) - start) - caretPos:caretPos <= start ? caretPos : candidatePreviewText.length - (preeditText.length - caretPos)]; + selRange:NSMakeRange(start, candidatePreviewText.length - (length - end) - start) + caretPos:caretPos <= start ? caretPos - 1 : candidatePreviewText.length - (length - caretPos)]; } else { if ((end < caretPos) && (caretPos > start)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; - } else if ((end < preeditText.length) && (caretPos < end)) { - candidatePreviewText = [candidatePreviewText substringWithRange:NSMakeRange(0, candidatePreviewText.length - (preeditText.length - end))]; + candidatePreviewText = [candidatePreviewText substringWithRange: + NSMakeRange(0, candidatePreviewText.length - (caretPos - end))]; + } else if ((end < length) && (caretPos < end)) { + candidatePreviewText = [candidatePreviewText substringWithRange: + NSMakeRange(0, candidatePreviewText.length - (length - end))]; } [self showPreeditString:candidatePreviewText selRange:NSMakeRange(start - (caretPos < end), candidatePreviewText.length - start + (caretPos < end)) - caretPos:caretPos < end ? caretPos - 1 : candidatePreviewText.length]; + caretPos:caretPos == 0 ? 0 : (caretPos < end ? caretPos - 1 : candidatePreviewText.length)]; } } else { - if (_inlinePreedit && !switcherMenu) { + if (_inlinePreedit && !_showingSwitcherMenu) { [self showPreeditString:preeditText selRange:NSMakeRange(start, end - start) caretPos:caretPos]; } else { // TRICKY: display a non-empty string to prevent iTerm2 from echoing each character in preedit. @@ -697,23 +707,24 @@ - (void)rimeUpdate } } // update candidates - NSMutableArray *candidates = [NSMutableArray array]; - NSMutableArray *comments = [NSMutableArray array]; - for (int i = 0; i < ctx.menu.num_candidates; ++i) { + NSUInteger numCandidate = RimeGetOption(_session, "ascii_mode") ? 0 : (NSUInteger)ctx.menu.num_candidates; + NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate]; + for (NSUInteger i = 0; i < numCandidate; ++i) { [candidates addObject:@(ctx.menu.candidates[i].text)]; [comments addObject:@(ctx.menu.candidates[i].comment ? : "")]; } - [self showPanelWithPreedit:(_inlinePreedit && !switcherMenu ? nil : preeditText) + [self showPanelWithPreedit:(_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText) selRange:NSMakeRange(start, end - start) - caretPos:(switcherMenu ? NSNotFound : caretPos) + caretPos:(_showingSwitcherMenu ? NSNotFound : caretPos) candidates:candidates comments:comments - highlighted:ctx.menu.highlighted_candidate_index - pageNum:ctx.menu.page_no - lastPage:ctx.menu.is_last_page]; + highlighted:(NSUInteger)ctx.menu.highlighted_candidate_index + pageNum:(NSUInteger)ctx.menu.page_no + lastPage:(BOOL)ctx.menu.is_last_page]; RimeFreeContext(&ctx); } else { - [NSApp.squirrelAppDelegate.panel hide]; + [self hidePalettes]; } } diff --git a/SquirrelPanel.h b/SquirrelPanel.h index 180b3e7be..928d4ab52 100644 --- a/SquirrelPanel.h +++ b/SquirrelPanel.h @@ -4,6 +4,12 @@ @class SquirrelConfig; @class SquirrelOptionSwitcher; +typedef enum { + defaultAppear = 0, + lightAppear = 0, + darkAppear = 1 +} SquirrelAppear; + @interface SquirrelPanel : NSPanel // Linear candidate list, as opposed to stacked candidate list. @@ -30,9 +36,7 @@ comments:(NSArray *)comments highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage - turnPage:(NSUInteger)turnPage - update:(BOOL)update; + lastPage:(BOOL)lastPage; - (void)hide; @@ -40,7 +44,7 @@ statusShort:(NSString *)messageShort; - (void)loadConfig:(SquirrelConfig *)config - forDarkMode:(BOOL)isDark; + forAppearance:(SquirrelAppear)appear; - (void)loadLabelConfig:(SquirrelConfig *)config; diff --git a/SquirrelPanel.m b/SquirrelPanel.m index d8b4d4342..0fed50a3a 100644 --- a/SquirrelPanel.m +++ b/SquirrelPanel.m @@ -20,12 +20,12 @@ - (CGPathRef)quartzPath { // Need to begin a path here. CGPathRef immutablePath = NULL; // Then draw the path elements. - NSUInteger numElements = self.elementCount; + NSInteger numElements = self.elementCount; if (numElements > 0) { CGMutablePathRef path = CGPathCreateMutable(); NSPoint points[3]; BOOL didClosePath = YES; - for (NSUInteger i = 0; i < numElements; i++) { + for (NSInteger i = 0; i < numElements; i++) { switch ([self elementAtIndex:i associatedPoints:points]) { case NSBezierPathElementMoveTo: CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); @@ -62,7 +62,8 @@ - (CGPathRef)quartzPath { } } -@end +@end // NSBezierPath (BezierPathQuartzUtilities) + @implementation NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) @@ -132,7 +133,7 @@ - (CGFloat)annotateRubyInRange:(NSRange)range // base string must use only one font so that all fall within one glyph run and the ruby annotation is aligned with no duplicates NSFont *baseFont = [self attribute:NSFontAttributeName atIndex:baseRange.location effectiveRange:NULL]; baseFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)baseFont, (CFStringRef)self.string, - CFRangeMake(baseRange.location, baseRange.length))); + CFRangeMake((int)baseRange.location, (int)baseRange.length))); [self addAttribute:NSFontAttributeName value:baseFont range:baseRange]; CGFloat rubyScale = 0.5; @@ -180,7 +181,8 @@ - (CGFloat)annotateRubyInRange:(NSRange)range return rubyLineHeight; } -@end +@end // NSMutableAttributedString (NSMutableAttributedStringMarkDownFormatting) + @interface SquirrelTheme : NSObject @@ -230,6 +232,7 @@ @interface SquirrelTheme : NSObject @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardFill; @property(nonatomic, strong, readonly) NSAttributedString *symbolForwardStroke; +@property(nonatomic, strong, readonly) NSString *selectKeys; @property(nonatomic, strong, readonly) NSArray *labels; @property(nonatomic, strong, readonly) NSArray *candidateFormats; @property(nonatomic, strong, readonly) NSArray *candidateHighlightedFormats; @@ -276,7 +279,8 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle pagingParagraphStyle:(NSParagraphStyle *)pagingParagraphStyle statusParagraphStyle:(NSParagraphStyle *)statusParagraphStyle; -- (void)setLabels:(NSArray *)labels; +- (void)setSelectKeys:(NSString *)selectKeys + labels:(NSArray *)labels; - (void)setCandidateFormat:(NSString *)candidateFormat; @@ -518,7 +522,9 @@ - (void)setParagraphStyle:(NSParagraphStyle *)paragraphStyle _statusParagraphStyle = statusParagraphStyle; } -- (void)setLabels:(NSArray *)labels { +- (void)setSelectKeys:(NSString *)selectKeys + labels:(NSArray *)labels { + _selectKeys = selectKeys; _labels = labels; } @@ -583,7 +589,8 @@ - (void)setAnnotationHeight:(CGFloat)height { } } -@end +@end // SquirrelTheme + @interface SquirrelLayoutManager : NSLayoutManager @end @@ -615,7 +622,7 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphRange matrix.ty = - origin.y - NSMinY(lineRect) - position.y; CGContextSetTextMatrix(context, matrix); CTRunDraw(run, context, CFRangeMake(0, 0)); - glyphIndex += CTRunGetGlyphCount(run); + glyphIndex += (NSUInteger)CTRunGetGlyphCount(run); } CGContextRestoreGState(context); CFRelease(line); @@ -659,7 +666,8 @@ - (NSRect) layoutManager:(NSLayoutManager *)layoutManager return NSMakeRect(glyphPosition.x, 0.0, width, glyphPosition.y); } -@end +@end // SquirrelLayoutManager + API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutFragment : NSTextLayoutFragment @@ -669,21 +677,17 @@ @implementation SquirrelTextLayoutFragment - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context { - BOOL isVertical = self.textLayoutManager.textContainer.layoutOrientation == NSTextLayoutOrientationVertical; NSArray *lineFragments = self.textLineFragments; for (NSTextLineFragment *lineFrag in lineFragments) { - CGRect lineRect = lineFrag.typographicBounds; NSFont *refFont = [lineFrag.attributedString attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:0 effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - NSParagraphStyle *rulerStyle = [lineFrag.attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:NULL]; - CGFloat lineHeight = rulerStyle.minimumLineHeight; - CGFloat refFontHeight = refFont.ascender - refFont.descender; CGPoint renderOrigin = CGPointMake(point.x + NSMinX(lineFrag.typographicBounds) + lineFrag.glyphOrigin.x, - point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y - refFontHeight / 2 + (isVertical ? 0.0 : lineHeight / 2 + refFont.descender)); + point.y + NSMaxY(lineFrag.typographicBounds) - lineFrag.glyphOrigin.y + refFont.descender); [lineFrag drawAtPoint:renderOrigin inContext:context]; } } -@end +@end // SquirrelTextLayoutFragment + API_AVAILABLE(macos(12.0)) @interface SquirrelTextLayoutManager : NSTextLayoutManager @@ -697,7 +701,8 @@ - (NSTextLayoutFragment *)textLayoutManager:(NSTextLayoutManager *)textLayoutMan return [[SquirrelTextLayoutFragment alloc] initWithTextElement:textElement range:textElement.elementRange]; } -@end +@end // SquirrelTextLayoutManager + @interface SquirrelView : NSView @@ -715,7 +720,7 @@ @interface SquirrelView : NSView @property(nonatomic, readonly) NSUInteger pagingButton; @property(nonatomic, readonly) CAShapeLayer *shape; @property(nonatomic, readonly, strong) SquirrelTheme *currentTheme; -@property(nonatomic, readonly) BOOL isDark; +@property(nonatomic, readonly) SquirrelAppear appear; - (void)drawViewWithInsets:(NSEdgeInsets)insets candidateRanges:(NSArray *)candidateRanges @@ -743,23 +748,24 @@ - (BOOL)wantsUpdateLayer { return YES; } -- (BOOL)isDark { - if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { - return YES; +- (SquirrelAppear)appear { + if ([NSApp.effectiveAppearance bestMatchFromAppearancesWithNames: + @[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua) { + return darkAppear; } - return NO; + return defaultAppear; } - (BOOL)allowsVibrancy { return YES; } -- (SquirrelTheme *)selectTheme:(BOOL)isDark { - return isDark ? _darkTheme : _defaultTheme; +- (SquirrelTheme *)selectTheme:(SquirrelAppear)appear { + return appear == darkAppear ? _darkTheme : _defaultTheme; } - (SquirrelTheme *)currentTheme { - return [self selectTheme:self.isDark]; + return [self selectTheme:self.appear]; } - (instancetype)initWithFrame:(NSRect)frameRect { @@ -815,8 +821,10 @@ - (NSTextRange *)getTextRangeFromCharRange:(NSRange)charRange API_AVAILABLE(maco return nil; } else { NSTextContentStorage *contentStorage = _textView.textContentStorage; - id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location withOffset:charRange.location]; - id endLocation = [contentStorage locationFromLocation:startLocation withOffset:charRange.length]; + id startLocation = [contentStorage locationFromLocation:contentStorage.documentRange.location + withOffset:(NSInteger)charRange.location]; + id endLocation = [contentStorage locationFromLocation:startLocation + withOffset:(NSInteger)charRange.length]; return [[NSTextRange alloc] initWithLocation:startLocation endLocation:endLocation]; } } @@ -1377,7 +1385,7 @@ - (void)updateLayer { } } if (theme.translucency > 0) { - panelLayer.opacity = 1.0 - theme.translucency; + panelLayer.opacity = (float)(1.0 - theme.translucency); } if (_highlightedIndex < _candidatePaths.count && theme.highlightedStripColor) { CAShapeLayer *highlightedLayer = [[CAShapeLayer alloc] init]; @@ -1426,13 +1434,15 @@ - (void)updateLayer { if (theme.tabled) { CAShapeLayer *horzGridLayer = [[CAShapeLayer alloc] init]; horzGridLayer.path = [candidateHorzGridPath quartzPath]; - horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + horzGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; horzGridLayer.lineWidth = theme.edgeInset.height / 2; horzGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:horzGridLayer]; CAShapeLayer *vertGridLayer = [[CAShapeLayer alloc] init]; vertGridLayer.path = [candidateVertGridPath quartzPath]; - vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction ofColor:(self.isDark ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; + vertGridLayer.strokeColor = [[theme.backgroundColor blendedColorWithFraction:kBlendedBackgroundColorFraction + ofColor:(self.appear == darkAppear ? [NSColor lightGrayColor] : [NSColor blackColor])] CGColor]; vertGridLayer.lineWidth = theme.edgeInset.width / 2; vertGridLayer.lineCap = kCALineCapRound; [panelLayer addSublayer:vertGridLayer]; @@ -1469,7 +1479,8 @@ - (BOOL)convertClickSpot:(NSPoint)spot toIndex:(NSUInteger *)index { return NO; } -@end +@end // SquirrelView + @implementation SquirrelPanel { SquirrelView *_view; @@ -1479,17 +1490,12 @@ @implementation SquirrelPanel { NSSize _maxSize; CGFloat _textWidthLimit; - NSString *_preedit; - NSRange _selRange; - NSUInteger _caretPos; - NSArray *_candidates; - NSArray *_comments; + NSUInteger _numCandidates; NSUInteger _index; - NSUInteger _pageNum; NSUInteger _turnPage; + BOOL _firstPage; BOOL _lastPage; - BOOL _mouseDown; NSPoint _scrollLocus; BOOL _initPosition; @@ -1521,8 +1527,8 @@ - (BOOL)inlineCandidate { return _view.currentTheme.inlineCandidate; } -- (void)initializeUIStyleForDarkMode:(BOOL)isDark { - SquirrelTheme *theme = [_view selectTheme:isDark]; +- (void)initializeUIStyleForAppearance:(SquirrelAppear)appear { + SquirrelTheme *theme = [_view selectTheme:appear]; NSFont *userFont = [NSFont fontWithDescriptor:getFontDescriptor([NSFont userFontOfSize:0.0].fontName) size:kDefaultFontSize]; @@ -1617,7 +1623,8 @@ - (void)initializeUIStyleForDarkMode:(BOOL)isDark { pagingParagraphStyle:pagingParagraphStyle statusParagraphStyle:statusParagraphStyle]; - [theme setLabels:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"0"]]; + [theme setSelectKeys:@"12345" + labels:@[@"1", @"2", @"3", @"4", @"5"]]; [theme setCandidateFormat:kDefaultCandidateFormat]; } @@ -1647,8 +1654,8 @@ - (instancetype)init { [contentView addSubview:_view.textView]; self.contentView = contentView; - [self initializeUIStyleForDarkMode:NO]; - [self initializeUIStyleForDarkMode:YES]; + [self initializeUIStyleForAppearance:defaultAppear]; + [self initializeUIStyleForAppearance:darkAppear]; _maxSize = NSZeroSize; _initPosition = YES; } @@ -1656,72 +1663,59 @@ - (instancetype)init { } - (void)sendEvent:(NSEvent *)event { + NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream + fromView:nil]; + NSUInteger cursorIndex = NSNotFound; switch (event.type) { - case NSEventTypeLeftMouseDown: - case NSEventTypeRightMouseDown: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if ((cursorIndex >= 0 && cursorIndex < _candidates.count) || - cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) { - _index = cursorIndex; - _mouseDown = YES; + case NSEventTypeLeftMouseUp: + if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index) { + rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; + [_inputController perform:kSELECT onIndex:indexChar]; + } else if (cursorIndex == _turnPage) { + rimeIndex indexChar = cursorIndex == NSPageUpFunctionKey ? kPageUp : + (cursorIndex == NSPageDownFunctionKey ? kPageDown : kVoidSymbol); + [_inputController perform:kSELECT onIndex:indexChar]; } } - } break; - case NSEventTypeLeftMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index || cursorIndex == _turnPage) { - [_inputController perform:kSELECT onIndex:cursorIndex]; - } - } - _mouseDown = NO; - } break; - case NSEventTypeRightMouseUp: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; - if (_mouseDown && [_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex == _index && (cursorIndex >= 0 && cursorIndex < _candidates.count)) { - [_inputController perform:kDELETE onIndex:cursorIndex]; + break; + case NSEventTypeRightMouseUp: + if (event.clickCount == 1 && [_view convertClickSpot:spot toIndex:&cursorIndex]) { + if (cursorIndex == _index) { + [_inputController perform:kDELETE onIndex:(rimeIndex)cursorIndex]; } } - _mouseDown = NO; - } break; - case NSEventTypeMouseEntered: { - self.acceptsMouseMovedEvents = YES; - } break; - case NSEventTypeMouseExited: { - self.acceptsMouseMovedEvents = NO; - } break; - case NSEventTypeMouseMoved: { - NSPoint spot = [_view convertPoint:self.mouseLocationOutsideOfEventStream - fromView:nil]; - NSUInteger cursorIndex = NSNotFound; + break; + case NSEventTypeMouseMoved: if ([_view convertClickSpot:spot toIndex:&cursorIndex]) { - if (cursorIndex >= 0 && cursorIndex < _candidates.count && _index != cursorIndex) { - [_inputController perform:kCHOOSE onIndex:cursorIndex]; + if (cursorIndex >= 0 && cursorIndex < _numCandidates && _index != cursorIndex) { _index = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:cursorIndex - pageNum:_pageNum lastPage:_lastPage turnPage:NSNotFound update:NO]; + rimeIndex indexChar = [_view.currentTheme.selectKeys characterAtIndex:cursorIndex]; + [_inputController perform:kCHOOSE onIndex:indexChar]; } else if ((cursorIndex == NSPageUpFunctionKey || cursorIndex == NSPageDownFunctionKey) && _turnPage != cursorIndex) { _turnPage = cursorIndex; - [self showPreedit:_preedit selRange:_selRange caretPos:_caretPos - candidates:_candidates comments:_comments highlighted:_index - pageNum:_pageNum lastPage:_lastPage turnPage:cursorIndex update:NO]; + if (_turnPage == NSPageUpFunctionKey) { + [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(_view.pagingRange.location, 1)]; + cursorIndex = _firstPage ? NSBeginFunctionKey : NSPageUpFunctionKey; + } else if (_turnPage == NSPageDownFunctionKey) { + [_view.textStorage addAttributes:_view.currentTheme.pagingHighlightedAttrs range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)]; + cursorIndex = _lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; + } + [_view drawViewWithInsets:_view.insets + candidateRanges:_view.candidateRanges + highlightedIndex:_view.highlightedIndex + preeditRange:_view.preeditRange + highlightedPreeditRange:_view.highlightedPreeditRange + pagingRange:_view.pagingRange + pagingButton:cursorIndex]; + [self show]; } } - } break; - case NSEventTypeLeftMouseDragged: { - _mouseDown = NO; + break; + case NSEventTypeLeftMouseDragged: _maxSize = NSZeroSize; // reset the remember_size references after moving the panel [self performWindowDragWithEvent:event]; - } break; + break; case NSEventTypeScrollWheel: { SquirrelTheme *theme = _view.currentTheme; CGFloat scrollThreshold = [theme.attrs[NSParagraphStyleAttributeName] maximumLineHeight] + @@ -1738,24 +1732,24 @@ - (void)sendEvent:(NSEvent *)event { } // compare accumulated locus length against threshold and limit paging to max once if (_scrollLocus.x > scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageDownFunctionKey : NSPageUpFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageDown : kPageUp)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y > scrollThreshold) { - [_inputController perform:kSELECT onIndex:NSPageUpFunctionKey]; + [_inputController perform:kSELECT onIndex:kPageUp]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.x < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:(theme.vertical ? NSPageUpFunctionKey : NSPageDownFunctionKey)]; + [_inputController perform:kSELECT onIndex:(theme.vertical ? kPageUp : kPageDown)]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } else if (_scrollLocus.y < -scrollThreshold) { - [_inputController perform:kSELECT onIndex:NSPageDownFunctionKey]; + [_inputController perform:kSELECT onIndex:kPageDown]; _scrollLocus = NSMakePoint(NSNotFound, NSNotFound); } } } break; default: + [super sendEvent:event]; break; } - [super sendEvent:event]; } - (void)getCurrentScreen { @@ -1780,7 +1774,7 @@ - (void)getTextWidthLimit { } if (theme.tabled) { CGFloat tabInterval = theme.separatorWidth * 2; - _textWidthLimit = round((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; + _textWidthLimit = floor((_textWidthLimit + theme.separatorWidth) / tabInterval / 2) * tabInterval * 2 - theme.separatorWidth; } _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX); } @@ -1788,7 +1782,7 @@ - (void)getTextWidthLimit { // Get the window size, it will be the dirtyRect in SquirrelView.drawRect - (void)show { NSAppearance *requestedAppearance = [NSAppearance appearanceNamed: - (_view.isDark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; + (_view.appear == darkAppear ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua)]; if (self.appearance != requestedAppearance) { self.appearance = requestedAppearance; } @@ -1797,7 +1791,6 @@ - (void)show { SquirrelTheme *theme = _view.currentTheme; NSTextContainer *textContainer = _view.textView.textContainer; NSEdgeInsets insets = _view.insets; - CGFloat offsetHeight = MAX(kOffsetHeight, round(MAX(NSWidth(_position), NSHeight(_position)) / 2)); CGFloat textWidthRatio = MIN(1.0, 1.0 / (theme.vertical ? 4 : 3) + [theme.attrs[NSFontAttributeName] pointSize] / 144.0); NSRect screenRect = _screen.visibleFrame; CGFloat textHeightLimit = (theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * textWidthRatio - insets.top - insets.bottom; @@ -1812,8 +1805,8 @@ - (void)show { } } if (theme.rememberSize) { // remember panel size (fix the top leading anchor of the panel in screen coordiantes) - if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + offsetHeight) - : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) + if ((theme.vertical ? (NSMinY(_position) - NSMinY(screenRect) <= NSHeight(screenRect) * textWidthRatio + kOffsetHeight) + : (sweepVertical ? (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) : (NSMinX(_position) + MAX(NSWidth(maxContentRect), _maxSize.width) + insets.right > NSMaxX(screenRect)))) && theme.lineLength == 0) { if (NSWidth(maxContentRect) >= _maxSize.width) { @@ -1823,8 +1816,8 @@ - (void)show { [textContainer setSize:NSMakeSize(_maxSize.width, textHeightLimit)]; } } - if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? offsetHeight : 0)) - : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : offsetHeight))) { + if (theme.vertical ? (NSMinX(_position) - NSMinX(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? kOffsetHeight : 0)) + : (NSMinY(_position) - NSMinY(screenRect) < MAX(NSHeight(maxContentRect), _maxSize.height) + insets.top + insets.bottom + (sweepVertical ? 0 : kOffsetHeight))) { if (NSHeight(maxContentRect) >= _maxSize.height) { _maxSize.height = NSHeight(maxContentRect); } else { @@ -1833,61 +1826,78 @@ - (void)show { } } - _initPosition |= NSIntersectsRect(self.frame, _position); NSRect windowRect; - if (theme.vertical) { - windowRect = NSMakeRect(NSMaxX(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, - NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, - NSHeight(maxContentRect) + insets.top + insets.bottom, - NSWidth(maxContentRect) + insets.left + insets.right); - _initPosition |= NSIntersectsRect(windowRect, _position); - if (_initPosition) { - // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa - if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + offsetHeight) { - windowRect.origin.y = NSMinY(_position) + (sweepVertical ? insets.left : -offsetHeight) - NSHeight(windowRect); - } else { - windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : offsetHeight); - } - // Make the right edge of candidate block fixed at the left of cursor - windowRect.origin.x = NSMinX(_position) - (sweepVertical ? offsetHeight : 0) - NSWidth(windowRect); - if (!sweepVertical && _view.preeditRange.length > 0) { - NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; - windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); - } + if (_statusMessage != nil) { // following system UI, middle-align status message with cursor + _initPosition = YES; + if (theme.vertical) { + windowRect.size.width = NSHeight(maxContentRect) + insets.top + insets.bottom; + windowRect.size.height = NSWidth(maxContentRect) + insets.left + insets.right; + } else { + windowRect.size.width = NSWidth(maxContentRect) + insets.left + insets.right; + windowRect.size.height =NSHeight(maxContentRect) + insets.top + insets.bottom; + } + if (sweepVertical) { // vertically centre-align (MidY) in screen coordinates + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - windowRect.size.width; + windowRect.origin.y = NSMidY(_position) - windowRect.size.height / 2; + } else { // horizontally centre-align (MidX) in screen coordinates + windowRect.origin.x = NSMidX(_position) - windowRect.size.width / 2; + windowRect.origin.y = NSMinY(_position) - kOffsetHeight - windowRect.size.height; } } else { - windowRect = NSMakeRect(NSMinX(self.frame), - NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, - NSWidth(maxContentRect) + insets.left + insets.right, - NSHeight(maxContentRect) + insets.top + insets.bottom); - _initPosition |= NSIntersectsRect(windowRect, _position); - if (_initPosition) { - if (sweepVertical) { - // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa - if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + offsetHeight) { - windowRect.origin.x = NSMinX(_position) - offsetHeight - NSWidth(windowRect); + if (theme.vertical) { // anchor is the top right corner in screen coordinates (MaxX, MaxY) + windowRect = NSMakeRect(NSMaxX(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSMaxY(self.frame) - NSWidth(maxContentRect) - insets.left - insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right); + _initPosition |= NSIntersectsRect(windowRect, _position); + if (_initPosition) { + // To avoid jumping up and down while typing, use the lower screen when typing on upper, and vice versa + if (NSMinY(_position) - NSMinY(screenRect) > NSHeight(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.y = NSMinY(_position) - (sweepVertical ? 0 : insets.left + kOffsetHeight) - NSWidth(maxContentRect) - insets.right; } else { - windowRect.origin.x = NSMaxX(_position) + offsetHeight; + windowRect.origin.y = NSMaxY(_position) + (sweepVertical ? 0 : kOffsetHeight); + } + // Make the right edge of candidate block fixed at the left of cursor + windowRect.origin.x = NSMinX(_position) - (sweepVertical ? kOffsetHeight : 0) - NSHeight(maxContentRect) - insets.top - insets.bottom; + if (!sweepVertical && _view.preeditRange.length > 0) { + NSRect preeditRect = [_view contentRectForRange:_view.preeditRange]; + windowRect.origin.x += round(NSHeight(preeditRect) + [theme.preeditAttrs[NSFontAttributeName] descender] + insets.top); + } + } + } else { // anchor is the top left corner in screen coordinates (MinX, MaxY) + windowRect = NSMakeRect(NSMinX(self.frame), + NSMaxY(self.frame) - NSHeight(maxContentRect) - insets.top - insets.bottom, + NSWidth(maxContentRect) + insets.left + insets.right, + NSHeight(maxContentRect) + insets.top + insets.bottom); + _initPosition |= NSIntersectsRect(windowRect, _position); + if (_initPosition) { + if (sweepVertical) { + // To avoid jumping left and right while typing, use the lefter screen when typing on righter, and vice versa + if (NSMinX(_position) - NSMinX(screenRect) > NSWidth(screenRect) * textWidthRatio + kOffsetHeight) { + windowRect.origin.x = NSMinX(_position) - kOffsetHeight - NSWidth(windowRect); + } else { + windowRect.origin.x = NSMaxX(_position) + kOffsetHeight; + } + windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); + } else { + windowRect.origin.x = NSMinX(_position) - insets.left; + windowRect.origin.y = NSMinY(_position) - kOffsetHeight - NSHeight(windowRect); } - windowRect.origin.y = NSMinY(_position) - NSHeight(windowRect); - } else { - windowRect.origin = NSMakePoint(NSMinX(_position) - insets.left, - NSMinY(_position) - offsetHeight - NSHeight(windowRect)); } } } if (NSMaxX(windowRect) > NSMaxX(screenRect)) { - windowRect.origin.x = (_initPosition && sweepVertical ? NSMinX(_position) - offsetHeight : NSMaxX(screenRect)) - NSWidth(windowRect); + windowRect.origin.x = (_initPosition && sweepVertical ? MIN(NSMinX(_position) - kOffsetHeight, NSMaxX(screenRect)) : NSMaxX(screenRect)) - NSWidth(windowRect); } if (NSMinX(windowRect) < NSMinX(screenRect)) { - windowRect.origin.x = _initPosition && sweepVertical ? NSMaxX(_position) + offsetHeight : NSMinX(screenRect); + windowRect.origin.x = _initPosition && sweepVertical ? MAX(NSMaxX(_position) + kOffsetHeight, NSMinX(screenRect)) : NSMinX(screenRect); } if (NSMinY(windowRect) < NSMinY(screenRect)) { - windowRect.origin.y = _initPosition && !sweepVertical ? NSMaxY(_position) + offsetHeight : NSMinY(screenRect); + windowRect.origin.y = _initPosition && !sweepVertical ? MAX(NSMaxY(_position) + kOffsetHeight, NSMinY(screenRect)) : NSMinY(screenRect); } if (NSMaxY(windowRect) > NSMaxY(screenRect)) { - windowRect.origin.y = (_initPosition && !sweepVertical ? NSMinY(_position) - offsetHeight : NSMaxY(screenRect)) - NSHeight(windowRect); + windowRect.origin.y = (_initPosition && !sweepVertical ? MIN(NSMinY(_position) - kOffsetHeight, NSMaxY(screenRect)) : NSMaxY(screenRect)) - NSHeight(windowRect); } if (theme.vertical) { @@ -1927,7 +1937,8 @@ - (void)show { } [self setAlphaValue:theme.alpha]; [self orderFront:nil]; - _initPosition = NO; + // reset to initial position after showing status message + _initPosition = _statusMessage != nil; // voila ! } @@ -1941,10 +1952,13 @@ - (void)hide { _initPosition = YES; } -- (void)setLayoutForRange:(NSRange)charRange - withReferenceFont:(NSFont *)refFont - paragraphStyle:(NSParagraphStyle *)style { +- (void)setLayoutForRange:(NSRange)charRange { BOOL verticalLayout = _view.currentTheme.vertical; + NSFont *refFont = [_view.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) + atIndex:charRange.location + effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; + NSParagraphStyle *style = [_view.textStorage attribute:NSParagraphStyleAttributeName + atIndex:charRange.location effectiveRange:NULL]; CGFloat refFontHeight = refFont.ascender - refFont.descender; CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, style.minimumLineHeight); @@ -1954,16 +1968,20 @@ - (void)setLayoutForRange:(NSRange)charRange enumerateAttributesInRange:charRange options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { - NSFont *refFont = attrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)][CFBridgingRelease(kCTBaselineReferenceFont)]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; CGFloat baselineOffset = [attrs[NSBaselineOffsetAttributeName] doubleValue] + lineHeight / 2 - refFontHeight / 2; - NSNumber *superscript = attrs[NSSuperscriptAttributeName]; - if (superscript) { - NSFont *runFont = verticalLayout ? [attrs[NSFontAttributeName] verticalFont] : attrs[NSFontAttributeName]; - baselineOffset += superscript.integerValue == 1 ? runFont.descender / 3 : runFont.ascender / 3; + NSFont *runFont = attrs[NSFontAttributeName]; + NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; + if (superscript != 0) { + baselineOffset += superscript == 1 ? runFont.ascender + runFont.descender : -runFont.descender; + } + if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + if (!verticalLayout) { + baselineOffset -= (runFont.ascender - runFont.descender) / 16; + } else if (superscript == 1) { + baselineOffset -= runFont.ascender + runFont.descender - (runFont.ascender - runFont.descender) / 16; + } else if (superscript == -1) { + baselineOffset += runFont.descender - (runFont.ascender - runFont.descender) / 16; + } } [_view.textStorage addAttribute:NSBaselineOffsetAttributeName value:@(baselineOffset) range:range]; @@ -1973,11 +1991,6 @@ - (void)setLayoutForRange:(NSRange)charRange NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange actualCharacterRange:NULL]; [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange usingBlock:^(NSRect rect, NSRect usedRect, NSTextContainer *textContainer, NSRange range, BOOL *stop) { - NSFont *refFont = [layoutManager.textStorage attribute:CFBridgingRelease(kCTBaselineReferenceInfoAttributeName) atIndex:range.location effectiveRange:NULL][CFBridgingRelease(kCTBaselineReferenceFont)]; - CGFloat refFontHeight = refFont.ascender - refFont.descender; - CGFloat lineHeight = MAX(style.lineHeightMultiple > 0 ? refFontHeight * style.lineHeightMultiple : refFontHeight, - style.minimumLineHeight); - lineHeight = style.maximumLineHeight > 0 ? MIN(lineHeight, style.maximumLineHeight) : lineHeight; CGFloat alignment = usedRect.origin.y - rect.origin.y + (verticalLayout ? lineHeight / 2 : refFont.ascender + lineHeight / 2 - refFontHeight / 2); // typesetting glyphs NSUInteger j = range.location; @@ -1987,29 +2000,28 @@ - (void)setLayoutForRange:(NSRange)charRange NSRange runRange = [layoutManager rangeOfNominallySpacedGlyphsContainingIndex:j]; NSDictionary *attrs = [layoutManager.textStorage attributesAtIndex:runCharLocation effectiveRange:NULL]; NSFont *runFont = attrs[NSFontAttributeName]; - NSFont *systemFont = [NSFont systemFontOfSize:runFont.pointSize]; NSString *baselineClass = attrs[CFBridgingRelease(kCTBaselineClassAttributeName)]; NSNumber *baselineOffset = attrs[NSBaselineOffsetAttributeName]; CGFloat offset = baselineOffset ? baselineOffset.doubleValue : 0.0; - NSNumber *superscript = attrs[NSSuperscriptAttributeName]; + NSInteger superscript = [attrs[NSSuperscriptAttributeName] integerValue]; if (verticalLayout) { NSNumber *verticalGlyph = attrs[NSVerticalGlyphFormAttributeName]; if (verticalGlyph ? verticalGlyph.boolValue : YES) { runFont = runFont.verticalFont; - systemFont = systemFont.verticalFont; } } - CGFloat runFontHeight = runFont.ascender - runFont.descender; - CGFloat systemFontHeight = systemFont.ascender - systemFont.descender; - if (superscript) { - offset += superscript.integerValue == 1 ? refFont.ascender / 3 : refFont.descender / 3; + if (superscript != 0) { + offset += superscript == 1 ? refFont.ascender - runFont.ascender : refFont.descender - runFont.descender; + if ([runFont.fontName isEqualToString:@"AppleColorEmoji"]) { + offset += superscript == 1 ? runFont.pointSize / 16 : runFont.descender + runFont.pointSize * 219 / 800; + } } if (verticalLayout) { if ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassRoman)] || !runFont.vertical) { runGlyphPosition.y = alignment - offset + refFont.xHeight / 2; } else { - runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) / 3 : 0.0); - runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? (runFontHeight - systemFontHeight) * 2 / 3 : 0.0; + runGlyphPosition.y = alignment - offset + ([runFont.fontName isEqualToString:@"AppleColorEmoji"] && superscript == 0 ? runFont.pointSize / 16 : 0.0); + runGlyphPosition.x += [runFont.fontName isEqualToString:@"AppleColorEmoji"] ? runFont.pointSize * 119 / 800 : 0.0; } } else { runGlyphPosition.y = alignment - offset + ([baselineClass isEqualToString:CFBridgingRelease(kCTBaselineClassIdeographicCentered)] ? runFont.xHeight / 2 - refFont.xHeight / 2 : 0.0); @@ -2079,23 +2091,14 @@ - (void)showPreedit:(NSString *)preedit comments:(NSArray *)comments highlighted:(NSUInteger)index pageNum:(NSUInteger)pageNum - lastPage:(BOOL)lastPage - turnPage:(NSUInteger)turnPage - update:(BOOL)update { - if (update) { - _preedit = preedit; - _selRange = selRange; - _caretPos = caretPos; - _candidates = candidates; - _comments = comments; - _index = index; - _pageNum = pageNum; - _lastPage = lastPage; - } - + lastPage:(BOOL)lastPage { [self getTextWidthLimit]; - NSUInteger numCandidates = candidates.count; - if (numCandidates > 0 || (preedit && preedit.length)) { + _numCandidates = candidates.count; + _index = _numCandidates == 0 ? NSNotFound : index; + _firstPage = pageNum == 0; + _lastPage = lastPage; + _turnPage = NSNotFound; + if (_numCandidates > 0 || (preedit && preedit.length)) { _statusMessage = nil; if (_statusTimer) { [_statusTimer invalidate]; @@ -2111,16 +2114,6 @@ - (void)showPreedit:(NSString *)preedit return; } - if (numCandidates == 0) { - _index = index = NSNotFound; - } - _turnPage = turnPage; - if (_turnPage == NSPageUpFunctionKey) { - turnPage = pageNum ? NSPageUpFunctionKey : NSBeginFunctionKey; - } else if (_turnPage == NSPageDownFunctionKey) { - turnPage = lastPage ? NSEndFunctionKey : NSPageDownFunctionKey; - } - SquirrelTheme *theme = _view.currentTheme; _view.textView.layoutOrientation = theme.vertical ? NSTextLayoutOrientationVertical : NSTextLayoutOrientationHorizontal; @@ -2132,7 +2125,7 @@ - (void)showPreedit:(NSString *)preedit [text setAttributedString:[[NSMutableAttributedString alloc] init]]; NSRange preeditRange = NSMakeRange(NSNotFound, 0); NSRange highlightedPreeditRange = NSMakeRange(NSNotFound, 0); - NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:numCandidates]; + NSMutableArray *candidateRanges = [[NSMutableArray alloc] initWithCapacity:_numCandidates]; NSRange pagingRange = NSMakeRange(NSNotFound, 0); NSMutableParagraphStyle *paragraphStyleCandidate; @@ -2159,13 +2152,13 @@ - (void)showPreedit:(NSString *)preedit } // force caret to be rendered horizontally in vertical layout if (caretPos != NSNotFound) { - [preeditLine addAttributes:@{NSVerticalGlyphFormAttributeName: @NO} - range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; + [preeditLine addAttribute:NSVerticalGlyphFormAttributeName value:@NO + range:NSMakeRange(caretPos - (caretPos < NSMaxRange(selRange)), 1)]; } preeditRange = NSMakeRange(0, preeditLine.length); [text appendAttributedString:preeditLine]; - if (numCandidates > 0) { + if (_numCandidates > 0) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.preeditAttrs]]; } else { goto typesetter; @@ -2179,7 +2172,7 @@ - (void)showPreedit:(NSString *)preedit paragraphStyleCandidate = [theme.paragraphStyle copy]; } CGFloat tabInterval = theme.separatorWidth * 2; - for (NSUInteger idx = 0; idx < numCandidates; ++idx) { + for (NSUInteger idx = 0; idx < _numCandidates; ++idx) { // attributed labels are already included in candidateFormats NSMutableAttributedString *item = (idx == index) ? [theme.candidateHighlightedFormats[idx] mutableCopy] : [theme.candidateFormats[idx] mutableCopy]; NSRange candidateRange = [item.string rangeOfString:@"%@"]; @@ -2226,7 +2219,7 @@ - (void)showPreedit:(NSString *)preedit NSMutableAttributedString *separator = [theme.separator mutableCopy]; if (theme.tabled) { // fill gaps to make cells 2^n tabs wide CGFloat widthInTabs = (ceil([text attributedSubstringFromRange:candidateRanges.lastObject.rangeValue].size.width) + theme.separatorWidth) / tabInterval; - NSUInteger numPaddingTabs = ceil(widthInTabs / 2) * 2 - ceil(widthInTabs); + NSUInteger numPaddingTabs = (NSUInteger)(ceil(widthInTabs / 2) * 2 - ceil(widthInTabs)); [separator replaceCharactersInRange:NSMakeRange(2, 0) withString:[@"\t" stringByPaddingToLength:numPaddingTabs withString:@"\t" startingAtIndex:0]]; } [text appendAttributedString:separator]; @@ -2241,7 +2234,7 @@ - (void)showPreedit:(NSString *)preedit } // for linear layout, middle-truncate candidates that are longer than one line if (theme.linear && ceil(item.size.width + theme.separatorWidth) > _textWidthLimit) { - if (idx < numCandidates - 1 || theme.showPaging) { + if (idx < _numCandidates - 1 || theme.showPaging) { [text appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:theme.commentAttrs]]; } NSMutableParagraphStyle *paragraphStyleTruncating = [paragraphStyleCandidate mutableCopy]; @@ -2262,11 +2255,6 @@ - (void)showPreedit:(NSString *)preedit initWithString:[NSString stringWithFormat:@" %lu ", pageNum + 1] attributes:theme.pagingAttrs]]; [paging appendAttributedString:[[NSAttributedString alloc] initWithAttributedString:(lastPage ? theme.symbolForwardStroke : theme.symbolForwardFill)]]; - if (_turnPage == NSPageUpFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(0, 1)]; - } else if (_turnPage == NSPageDownFunctionKey) { - [paging addAttributes:theme.pagingHighlightedAttrs range:NSMakeRange(paging.length - 1, 1)]; - } [text appendAttributedString:theme.separator]; NSUInteger pagingStart = text.length; @@ -2315,22 +2303,14 @@ - (void)showPreedit:(NSString *)preedit theme.edgeInset.height + theme.linespace / 2, theme.edgeInset.width + theme.separatorWidth / 2); if (preedit) { - [self setLayoutForRange:preeditRange - withReferenceFont:(theme.vertical ? [theme.preeditAttrs[NSFontAttributeName] verticalFont] : theme.preeditAttrs[NSFontAttributeName]) - paragraphStyle:theme.preeditParagraphStyle]; + [self setLayoutForRange:preeditRange]; insets.top = theme.edgeInset.height; } - if (numCandidates > 0) { + if (_numCandidates > 0) { NSRange candidateBlockRange = NSMakeRange(candidateBlockStart, (!theme.linear && pagingRange.length > 0 ? pagingRange.location : text.length) - candidateBlockStart); - NSFont *refFont = getTallestFont(@[theme.attrs[NSFontAttributeName], theme.labelAttrs[NSFontAttributeName], - theme.commentAttrs[NSFontAttributeName]], theme.vertical); - [self setLayoutForRange:candidateBlockRange - withReferenceFont:(theme.vertical ? refFont.verticalFont : refFont) - paragraphStyle:theme.paragraphStyle]; + [self setLayoutForRange:candidateBlockRange]; if (!theme.linear && pagingRange.length > 0) { - [self setLayoutForRange:pagingRange - withReferenceFont:theme.pagingAttrs[NSFontAttributeName] - paragraphStyle:theme.pagingParagraphStyle]; + [self setLayoutForRange:pagingRange]; insets.bottom = theme.edgeInset.height; } } else { @@ -2346,7 +2326,7 @@ - (void)showPreedit:(NSString *)preedit preeditRange:preeditRange highlightedPreeditRange:highlightedPreeditRange pagingRange:pagingRange - pagingButton:turnPage]; + pagingButton:_turnPage]; [self show]; } @@ -2381,9 +2361,7 @@ - (void)showStatus:(NSString *)message { [text setAttributedString:[[NSMutableAttributedString alloc] initWithString:message attributes:theme.statusAttrs]]; [text ensureAttributesAreFixedInRange:NSMakeRange(0, text.length)]; - [self setLayoutForRange:NSMakeRange(0, text.length) - withReferenceFont:(theme.vertical ? [theme.statusAttrs[NSFontAttributeName] verticalFont] : theme.statusAttrs[NSFontAttributeName]) - paragraphStyle:theme.statusParagraphStyle]; + [self setLayoutForRange:NSMakeRange(0, text.length)]; // disable remember_size and fixed line_length for status messages _initPosition = YES; @@ -2533,50 +2511,51 @@ - (void)setAnnotationHeight:(CGFloat)height { } - (void)loadLabelConfig:(SquirrelConfig *)config { - SquirrelTheme *theme = [_view selectTheme:NO]; + SquirrelTheme *theme = [_view selectTheme:defaultAppear]; [SquirrelPanel updateTheme:theme withLabelConfig:config]; - SquirrelTheme *darkTheme = [_view selectTheme:YES]; + SquirrelTheme *darkTheme = [_view selectTheme:darkAppear]; [SquirrelPanel updateTheme:darkTheme withLabelConfig:config]; } + (void)updateTheme:(SquirrelTheme *)theme withLabelConfig:(SquirrelConfig *)config { - int menuSize = [config getInt:@"menu/page_size"] ? : 5; + NSUInteger menuSize = (NSUInteger)[config getInt:@"menu/page_size"] ? : 5; NSMutableArray *labels = [[NSMutableArray alloc] initWithCapacity:menuSize]; NSString *selectKeys = [config getString:@"menu/alternative_select_keys"]; if (selectKeys) { NSString *keyCaps = [[selectKeys uppercaseString] stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; - for (int i = 0; i < menuSize; ++i) { + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [keyCaps substringWithRange:NSMakeRange(i, 1)]; } } else { + selectKeys = [@"1234567890" substringToIndex:menuSize]; NSArray *selectLabels = [config getList:@"menu/alternative_select_labels"]; if (selectLabels) { - for (int i = 0; i < menuSize; ++i) { + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = selectLabels[i]; } } else { - NSString *numerals = @"1234567890"; - for (int i = 0; i < menuSize; ++i) { + NSString *numerals = [selectKeys stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth reverse:YES]; + for (NSUInteger i = 0; i < menuSize; ++i) { labels[i] = [numerals substringWithRange:NSMakeRange(i, 1)]; } } } - [theme setLabels:labels]; + [theme setSelectKeys:selectKeys labels:labels]; } - (void)loadConfig:(SquirrelConfig *)config - forDarkMode:(BOOL)isDark { - SquirrelTheme *theme = [_view selectTheme:isDark]; + forAppearance:(SquirrelAppear)appear { + SquirrelTheme *theme = [_view selectTheme:appear]; NSSet *styleOptions = [NSSet setWithArray:self.optionSwitcher.optionStates]; - [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forDarkMode:isDark]; + [SquirrelPanel updateTheme:theme withConfig:config styleOptions:styleOptions forAppearance:appear]; } + (void)updateTheme:(SquirrelTheme *)theme withConfig:(SquirrelConfig *)config styleOptions:(NSSet *)styleOptions - forDarkMode:(BOOL)isDark { + forAppearance:(SquirrelAppear)appear { // INTERFACE BOOL linear = NO; BOOL tabled = NO; @@ -2623,7 +2602,7 @@ + (void)updateTheme:(SquirrelTheme *)theme NSColor *highlightedCommentTextColor; NSString *colorScheme; - if (isDark) { + if (appear == darkAppear) { for (NSString *option in styleOptions) { if ((colorScheme = [config getString:[NSString stringWithFormat:@"style/%@/color_scheme_dark", option]])) break; } @@ -2709,6 +2688,8 @@ + (void)updateTheme:(SquirrelTheme *)theme fontDescriptorByAddingAttributes:monoDigitAttrs]; NSFont *labelFont = labelFontDescriptor ? [NSFont fontWithDescriptor:labelFontDescriptor size:MAX(labelFontSize.doubleValue, 0)] : [NSFont monospacedDigitSystemFontOfSize:MAX(labelFontSize.doubleValue, 0) weight:NSFontWeightRegular]; + NSString *labelString = [theme.labels componentsJoinedByString:@""]; + labelFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)labelFont, (CFStringRef)labelString, CFRangeMake(0, (int)labelString.length))); NSFontDescriptor *commentFontDescriptor = getFontDescriptor(commentFontName); NSFont *commentFont = [NSFont fontWithDescriptor:(commentFontDescriptor ? : fontDescriptor) @@ -2774,8 +2755,9 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSFontAttributeName] = linear ? labelFont : pagingFont; statusAttrs[NSFontAttributeName] = commentFont; - NSFont *refFont = getTallestFont(@[font, labelFont, commentFont], vertical); - refFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)refFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *zhFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)font, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *zhCommentFont = CFBridgingRelease(CTFontCreateForString((CTFontRef)commentFont, (CFStringRef)kFullWidthSpace, CFRangeMake(0, 1))); + NSFont *refFont = getTallestFont(@[zhFont, labelFont, zhCommentFont], vertical); labelAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); labelHighlightedAttrs[CFBridgingRelease(kCTBaselineClassAttributeName)] = CFBridgingRelease(kCTBaselineClassIdeographicCentered); labelAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; @@ -2784,10 +2766,10 @@ + (void)updateTheme:(SquirrelTheme *)theme highlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; commentAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; commentHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; - preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? refFont.verticalFont : refFont}; + preeditAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; + preeditHighlightedAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhFont.verticalFont : zhFont}; pagingAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): linear ? labelFont : pagingFont}; - statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? commentFont.verticalFont : commentFont}; + statusAttrs[CFBridgingRelease(kCTBaselineReferenceInfoAttributeName)] = @{CFBridgingRelease(kCTBaselineReferenceFont): vertical ? zhCommentFont.verticalFont : zhCommentFont}; attrs[NSBaselineOffsetAttributeName] = baseOffset; highlightedAttrs[NSBaselineOffsetAttributeName] = baseOffset; @@ -2809,7 +2791,7 @@ + (void)updateTheme:(SquirrelTheme *)theme pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO); // CHROMATICS refinement - if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - isDark) <= 0.4) { + if (theme.translucency > 0 && ABS(backgroundColor.brightnessComponent - (appear == darkAppear)) <= 0.4) { backgroundColor = inverseColor(backgroundColor); borderColor = inverseColor(borderColor); preeditBackgroundColor = inverseColor(preeditBackgroundColor); @@ -2862,7 +2844,7 @@ + (void)updateTheme:(SquirrelTheme *)theme preeditLinespace:MAX(spacing.doubleValue, 0) alpha:(alpha ? MIN(MAX(alpha.doubleValue, 0.0), 1.0) : 1.0) translucency:(translucency ? MIN(MAX(translucency.doubleValue, 0.0), 1.0) : 0.0) - lineLength:lineLength.doubleValue ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 + lineLength:lineLength.doubleValue > 0 ? MAX(lineLength.doubleValue, separatorWidth * 5) : 0.0 showPaging:showPaging.boolValue rememberSize:rememberSize.boolValue tabled:tabled @@ -2899,4 +2881,4 @@ + (void)updateTheme:(SquirrelTheme *)theme [theme setStatusMessageType:statusMessageType]; } -@end +@end // SquirrelPanel diff --git a/input_source.m b/input_source.m index d13664fc2..230ad01c2 100644 --- a/input_source.m +++ b/input_source.m @@ -1,13 +1,13 @@ #import -static const unsigned char kInstallLocation[] = +static const char kInstallLocation[] = "/Library/Input Methods/Squirrel.app"; -static NSString *const kHansInputModeID = +static NSString *kHansInputModeID = @"im.rime.inputmethod.Squirrel.Hans"; -static NSString *const kHantInputModeID = +static NSString *kHantInputModeID = @"im.rime.inputmethod.Squirrel.Hant"; -static NSString *const kCantInputModeID = +static NSString *kCantInputModeID = @"im.rime.inputmethod.Squirrel.Cant"; #define HANS_INPUT_MODE (1 << 0) @@ -16,7 +16,7 @@ void RegisterInputSource(void) { CFURLRef installedLocationURL = CFURLCreateFromFileSystemRepresentation( - NULL, kInstallLocation, strlen((const char *)kInstallLocation), NO); + NULL, (UInt8 *)kInstallLocation, (CFIndex)strlen(kInstallLocation), false); if (installedLocationURL) { TISRegisterInputSource(installedLocationURL); CFRelease(installedLocationURL); diff --git a/librime b/librime index e69533de7..b16c98fad 160000 --- a/librime +++ b/librime @@ -1 +1 @@ -Subproject commit e69533de741d838340dc3f253a6776229f48e70b +Subproject commit b16c98fad54adc57190d48b3d8a90890ffbe1a98 diff --git a/macos_keycode.m b/macos_keycode.m index 32eefe081..de427b026 100644 --- a/macos_keycode.m +++ b/macos_keycode.m @@ -43,7 +43,7 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { //OSX_VK_ENTER_POWERBOOK -> ? { OSX_VK_ESCAPE, XK_Escape }, { OSX_VK_FORWARD_DELETE, XK_Delete }, - //{OSX_VK_HELP, XK_Help}, // the same keycode with OSX_VK_PC_INSERT + //{OSX_VK_HELP, XK_Help}, // the same keycode as OSX_VK_PC_INSERT { OSX_VK_RETURN, XK_Return }, { OSX_VK_SPACE, XK_space }, { OSX_VK_TAB, XK_Tab }, @@ -102,12 +102,15 @@ int osx_modifiers_to_rime_modifiers(unsigned long modifiers) { // pc keyboard { OSX_VK_PC_APPLICATION, XK_Menu }, { OSX_VK_PC_INSERT, XK_Insert }, - { OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock }, + //{OSX_VK_PC_KEYPAD_NUMLOCK, XK_Num_Lock}, // the same keycode as OSX_VK_KEYPAD_CLEAR { OSX_VK_PC_PAUSE, XK_Pause }, //OSX_VK_PC_POWER -> ? { OSX_VK_PC_PRINTSCREEN, XK_Print }, { OSX_VK_PC_SCROLLLOCK, XK_Scroll_Lock }, + // JIS keyboard + { OSX_VK_JIS_EISUU, XK_Eisu_toggle }, + { -1, -1 } }; diff --git a/main.m b/main.m index 9926baefc..ec6be682d 100644 --- a/main.m +++ b/main.m @@ -14,7 +14,7 @@ // Each input method needs a unique connection name. // Note that periods and spaces are not allowed in the connection name. -const NSString *kConnectionName = @"Squirrel_1_Connection"; +static NSString *kConnectionName = @"Squirrel_1_Connection"; int main(int argc, char *argv[]) { if (argc > 1 && !strcmp("--quit", argv[1])) { @@ -65,12 +65,12 @@ int main(int argc, char *argv[]) { // find the bundle identifier and then initialize the input method server NSBundle *main = [NSBundle mainBundle]; IMKServer *server __unused = - [[IMKServer alloc] initWithName:(NSString *)kConnectionName + [[IMKServer alloc] initWithName:kConnectionName bundleIdentifier:main.bundleIdentifier]; // load the bundle explicitly because in this case the input method is a // background only application - [main loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication] topLevelObjects:NULL]; + [main loadNibNamed:@"MainMenu" owner:[NSApplication sharedApplication] topLevelObjects:nil]; // opencc will be configured with relative dictionary paths [[NSFileManager defaultManager]