diff --git a/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg
index 3a839c4d9..25fd848bf 100644
--- a/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg
+++ b/Assets.xcassets/Symbols/delete.backward.fill.symbolset/delete.backward.fill.svg
@@ -71,8 +71,8 @@ PUBLIC "-//W3C//DTD SVG 1.1//EN"
-
-
+
+
diff --git a/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg b/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg
index e4630ab4d..a2eed5af5 100644
--- a/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg
+++ b/Assets.xcassets/Symbols/delete.backward.symbolset/delete.backward.svg
@@ -71,8 +71,8 @@ PUBLIC "-//W3C//DTD SVG 1.1//EN"
-
-
+
+
diff --git a/Squirrel.xcodeproj/project.pbxproj b/Squirrel.xcodeproj/project.pbxproj
index 5a3e2958a..dc9da5ce2 100644
--- a/Squirrel.xcodeproj/project.pbxproj
+++ b/Squirrel.xcodeproj/project.pbxproj
@@ -638,6 +638,7 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
@@ -701,6 +702,7 @@
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES;
CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
diff --git a/SquirrelApplicationDelegate.hh b/SquirrelApplicationDelegate.hh
index fc542f422..6f42e7ee1 100644
--- a/SquirrelApplicationDelegate.hh
+++ b/SquirrelApplicationDelegate.hh
@@ -1,4 +1,5 @@
#import
+#import "rime_api.h"
@class SquirrelConfig;
@class SquirrelPanel;
@@ -8,14 +9,12 @@
// outlet of NSApp's instance
@interface SquirrelApplicationDelegate : NSObject
-typedef NS_ENUM(NSUInteger, SquirrelNotificationPolicy) {
+typedef NS_CLOSED_ENUM(NSUInteger, SquirrelNotificationPolicy) {
kShowNotificationsNever = 0,
kShowNotificationsWhenAppropriate = 1,
kShowNotificationsAlways = 2
};
-typedef uintptr_t RimeSessionId;
-
@property(nonatomic, weak, nullable) IBOutlet NSMenu* menu;
@property(nonatomic, weak, nullable) IBOutlet SquirrelPanel* panel;
@property(nonatomic, weak, nullable) IBOutlet id updater;
diff --git a/SquirrelApplicationDelegate.mm b/SquirrelApplicationDelegate.mm
index 2838d1ef0..19d84bf6a 100644
--- a/SquirrelApplicationDelegate.mm
+++ b/SquirrelApplicationDelegate.mm
@@ -3,7 +3,6 @@
#import "SquirrelConfig.hh"
#import "SquirrelPanel.hh"
#import "macos_keycode.hh"
-#import "rime_api.h"
#import
static NSString* const kRimeWikiURL = @"https://github.com/rime/home/wiki";
@@ -15,7 +14,7 @@ @implementation SquirrelApplicationDelegate {
- (IBAction)showSwitcher:(id)sender {
NSLog(@"Show Switcher");
- if (_switcherKeyEquivalent) {
+ if (_switcherKeyEquivalent != 0) {
RimeSessionId session = [sender unsignedLongValue];
rime_get_api()->process_key(session, _switcherKeyEquivalent,
_switcherKeyModifierMask);
@@ -46,13 +45,20 @@ - (IBAction)openWiki:(id)sender {
}
- (IBAction)openLogFolder:(id)sender {
- NSURL* logFile = [NSFileManager.defaultManager.temporaryDirectory
+ NSURL* infoLog = [NSFileManager.defaultManager.temporaryDirectory
URLByAppendingPathComponent:@"rime.squirrel.INFO"
isDirectory:NO];
- [NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:@[ logFile ]];
+ NSURL* warningLog = [NSFileManager.defaultManager.temporaryDirectory
+ URLByAppendingPathComponent:@"rime.squirrel.WARNING"
+ isDirectory:NO];
+ NSURL* errorLog = [NSFileManager.defaultManager.temporaryDirectory
+ URLByAppendingPathComponent:@"rime.squirrel.ERROR"
+ isDirectory:NO];
+ [NSWorkspace.sharedWorkspace
+ activateFileViewerSelectingURLs:@[ infoLog, warningLog, errorLog ]];
}
-void show_notification(const char* msg_text) {
+extern void show_notification(const char* msg_text) {
if (@available(macOS 10.14, *)) {
UNUserNotificationCenter* center =
UNUserNotificationCenter.currentNotificationCenter;
@@ -61,7 +67,7 @@ void show_notification(const char* msg_text) {
UNAuthorizationOptionProvisional
completionHandler:^(BOOL granted,
NSError* _Nullable error) {
- if (error) {
+ if (error != nil) {
NSLog(@"User notification authorization error: %@",
error.debugDescription);
}
@@ -84,7 +90,7 @@ void show_notification(const char* msg_text) {
content:content
trigger:nil]
withCompletionHandler:^(NSError* _Nullable error) {
- if (error) {
+ if (error != nil) {
NSLog(@"User notification request error: %@",
error.debugDescription);
}
@@ -104,9 +110,9 @@ void show_notification(const char* msg_text) {
}
static void show_status(const char* msg_text_long, const char* msg_text_short) {
- NSString* msgLong = msg_text_long ? @(msg_text_long) : nil;
+ NSString* msgLong = msg_text_long != NULL ? @(msg_text_long) : nil;
NSString* msgShort =
- msg_text_short
+ msg_text_short != NULL
? @(msg_text_short)
: [msgLong substringWithRange:
[msgLong rangeOfComposedCharacterSequenceAtIndex:0]];
@@ -118,37 +124,34 @@ static void notification_handler(void* context_object,
RimeSessionId session_id,
const char* message_type,
const char* message_value) {
- if (!strcmp(message_type, "deploy")) {
- if (!strcmp(message_value, "start")) {
+ if (strcmp(message_type, "deploy") == 0) {
+ if (strcmp(message_value, "start") == 0) {
show_notification("deploy_start");
- } else if (!strcmp(message_value, "success")) {
+ } else if (strcmp(message_value, "success") == 0) {
show_notification("deploy_success");
- } else if (!strcmp(message_value, "failure")) {
+ } else if (strcmp(message_value, "failure") == 0) {
show_notification("deploy_failure");
}
return;
}
SquirrelApplicationDelegate* app_delegate = (__bridge id)context_object;
// schema change
- if (!strcmp(message_type, "schema") &&
+ if (strcmp(message_type, "schema") == 0 &&
app_delegate.showNotifications != kShowNotificationsNever) {
const char* schema_name = strchr(message_value, '/');
- if (schema_name) {
+ if (schema_name != NULL) {
++schema_name;
show_status(schema_name, schema_name);
}
return;
}
// option change
- if (!strcmp(message_type, "option") && app_delegate) {
+ if (strcmp(message_type, "option") == 0 && app_delegate) {
Bool state = message_value[0] != '!';
const char* option_name = message_value + !state;
+ BOOL updateScriptVariant = [app_delegate.panel.optionSwitcher
+ updateCurrentScriptVariant:@(message_value)];
BOOL updateStyleOptions = NO;
- BOOL updateScriptVariant = NO;
- if ([app_delegate.panel.optionSwitcher
- updateCurrentScriptVariant:@(message_value)]) {
- updateScriptVariant = YES;
- }
if ([app_delegate.panel.optionSwitcher updateGroupState:@(message_value)
ofOption:@(option_name)]) {
updateStyleOptions = YES;
@@ -167,7 +170,7 @@ static void notification_handler(void* context_object,
RimeStringSlice state_label_short =
rime_get_api()->get_state_label_abbreviated(session_id, option_name,
state, True);
- if (state_label_long.str || state_label_short.str) {
+ if (state_label_long.str != NULL || state_label_short.str != NULL) {
const char* short_message =
state_label_short.length < strlen(state_label_short.str)
? NULL
@@ -227,7 +230,7 @@ - (void)loadSettings {
if ([defaultConfig openWithConfigId:@"default"]) {
NSString* hotkey =
[defaultConfig getStringForOption:@"switcher/hotkeys/@0"];
- if (hotkey) {
+ if (hotkey != nil) {
NSArray* keys = [hotkey componentsSeparatedByString:@"+"];
for (NSUInteger i = 0; i < keys.count - 1; ++i) {
_switcherKeyModifierMask |=
@@ -304,7 +307,7 @@ - (BOOL)problematicLaunchDetected {
NSData* archive = [NSData dataWithContentsOfURL:logfile
options:NSDataReadingUncached
error:nil];
- if (archive) {
+ if (archive != nil) {
NSDate* previousLaunch =
[NSKeyedUnarchiver unarchivedObjectOfClass:NSDate.class
fromData:archive
@@ -348,7 +351,7 @@ - (void)inputSourceChanged:(NSNotification*)aNotification {
CFStringRef inputSource = (CFStringRef)TISGetInputSourceProperty(
TISCopyCurrentKeyboardInputSource(), kTISPropertyInputSourceID);
CFStringRef bundleId = CFBundleGetIdentifier(CFBundleGetMainBundle());
- if (!CFStringHasPrefix(inputSource, bundleId)) {
+ if (CFStringHasPrefix(inputSource, bundleId) == kCFCompareEqualTo) {
_isCurrentInputMethod = NO;
}
}
diff --git a/SquirrelConfig.hh b/SquirrelConfig.hh
index 6821301a1..1dcc5ffd8 100644
--- a/SquirrelConfig.hh
+++ b/SquirrelConfig.hh
@@ -1,6 +1,5 @@
#import
-
-typedef uintptr_t RimeSessionId;
+#import
__attribute__((objc_direct_members))
@interface SquirrelOptionSwitcher : NSObject
@@ -101,7 +100,7 @@ typedef NSDictionary SquirrelAppOptions;
- (NSUInteger)getListSizeForOption:(NSString* _Nonnull)option;
- (NSArray* _Nullable)getListForOption:(NSString* _Nonnull)option;
-- (SquirrelOptionSwitcher* _Nullable)getOptionSwitcher;
+- (SquirrelOptionSwitcher* _Nonnull)getOptionSwitcher;
- (SquirrelAppOptions* _Nonnull)getAppOptions:(NSString* _Nonnull)appName;
@end // SquirrelConfig
diff --git a/SquirrelConfig.mm b/SquirrelConfig.mm
index 45673ef75..b194cdf06 100644
--- a/SquirrelConfig.mm
+++ b/SquirrelConfig.mm
@@ -1,7 +1,5 @@
#import "SquirrelConfig.hh"
-#import
-
static NSArray* const scripts = @[
@"zh-Hans", @"zh-Hant", @"zh-TW", @"zh-HK", @"zh-MO", @"zh-SG", @"zh-CN",
@"zh"
@@ -17,8 +15,7 @@ @implementation SquirrelOptionSwitcher
defaultScriptVariant:(NSString*)defaultScriptVariant
scriptVariantOptions:
(NSDictionary*)scriptVariantOptions {
- self = [super init];
- if (self) {
+ if (self = [super init]) {
_schemaId = schemaId ?: @"";
_switcher = switcher ?: NSMutableDictionary.dictionary;
_optionGroups = optionGroups ?: NSDictionary.dictionary;
@@ -63,7 +60,7 @@ - (BOOL)updateSwitcher:(NSMutableDictionary*)switcher {
- (BOOL)updateGroupState:(NSString*)optionState ofOption:(NSString*)optionName {
NSOrderedSet* optionGroup = _optionGroups[optionName];
- if (!optionGroup) {
+ if (optionGroup == nil) {
return NO;
}
if (optionGroup.count == 1) {
@@ -87,7 +84,7 @@ - (BOOL)updateCurrentScriptVariant:(NSString*)scriptVariant {
return NO;
}
NSString* scriptVariantCode = _scriptVariantOptions[scriptVariant];
- if (!scriptVariantCode) {
+ if (scriptVariantCode == nil) {
return NO;
}
_currentScriptVariant = scriptVariantCode;
@@ -136,8 +133,7 @@ @implementation SquirrelConfig {
}
- (instancetype)init {
- self = [super init];
- if (self) {
+ if (self = [super init]) {
_cache = NSCache.alloc.init;
_colorSpace = NSColorSpace.sRGBColorSpace;
_colorSpaceName = @"sRGB";
@@ -284,14 +280,12 @@ - (NSNumber*)getOptionalDoubleForOption:(NSString*)option
}
- (NSNumber*)getOptionalBoolForOption:(NSString*)option alias:(NSString*)alias {
- NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(BOOL)
- forKey:option];
- if (cachedValue) {
+ if (NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(BOOL)
+ forKey:option]) {
return cachedValue;
}
- Bool value;
- if (_isOpen &&
- rime_get_api()->config_get_bool(&_config, option.UTF8String, &value)) {
+ if (Bool value; _isOpen && rime_get_api()->config_get_bool(
+ &_config, option.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithBool:(BOOL)value];
[_cache setObject:number forKey:option];
return number;
@@ -299,8 +293,8 @@ - (NSNumber*)getOptionalBoolForOption:(NSString*)option alias:(NSString*)alias {
if (alias != nil) {
NSString* aliasOption = [[option stringByDeletingLastPathComponent]
stringByAppendingPathComponent:alias.lastPathComponent];
- if (_isOpen && rime_get_api()->config_get_bool(
- &_config, aliasOption.UTF8String, &value)) {
+ if (Bool value; _isOpen && rime_get_api()->config_get_bool(
+ &_config, aliasOption.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithBool:(BOOL)value];
[_cache setObject:number forKey:option];
return number;
@@ -310,14 +304,12 @@ - (NSNumber*)getOptionalBoolForOption:(NSString*)option alias:(NSString*)alias {
}
- (NSNumber*)getOptionalIntForOption:(NSString*)option alias:(NSString*)alias {
- NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(int)
- forKey:option];
- if (cachedValue) {
+ if (NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(int)
+ forKey:option]) {
return cachedValue;
}
- int value;
- if (_isOpen &&
- rime_get_api()->config_get_int(&_config, option.UTF8String, &value)) {
+ if (int value; _isOpen && rime_get_api()->config_get_int(
+ &_config, option.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithInt:value];
[_cache setObject:number forKey:option];
return number;
@@ -325,8 +317,8 @@ - (NSNumber*)getOptionalIntForOption:(NSString*)option alias:(NSString*)alias {
if (alias != nil) {
NSString* aliasOption = [[option stringByDeletingLastPathComponent]
stringByAppendingPathComponent:alias.lastPathComponent];
- if (_isOpen && rime_get_api()->config_get_int(
- &_config, aliasOption.UTF8String, &value)) {
+ if (int value; _isOpen && rime_get_api()->config_get_int(
+ &_config, aliasOption.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithInt:value];
[_cache setObject:number forKey:option];
return number;
@@ -337,14 +329,12 @@ - (NSNumber*)getOptionalIntForOption:(NSString*)option alias:(NSString*)alias {
- (NSNumber*)getOptionalDoubleForOption:(NSString*)option
alias:(NSString*)alias {
- NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(double)
- forKey:option];
- if (cachedValue) {
+ if (NSNumber* cachedValue = [self cachedValueOfObjCType:@encode(double)
+ forKey:option]) {
return cachedValue;
}
- double value;
- if (_isOpen &&
- rime_get_api()->config_get_double(&_config, option.UTF8String, &value)) {
+ if (double value; _isOpen && rime_get_api()->config_get_double(
+ &_config, option.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithDouble:value];
[_cache setObject:number forKey:option];
return number;
@@ -352,7 +342,8 @@ - (NSNumber*)getOptionalDoubleForOption:(NSString*)option
if (alias != nil) {
NSString* aliasOption = [[option stringByDeletingLastPathComponent]
stringByAppendingPathComponent:alias.lastPathComponent];
- if (_isOpen && rime_get_api()->config_get_double(
+ if (double value;
+ _isOpen && rime_get_api()->config_get_double(
&_config, aliasOption.UTF8String, &value)) {
NSNumber* number = [NSNumber numberWithDouble:value];
[_cache setObject:number forKey:option];
@@ -382,15 +373,14 @@ - (NSImage*)getImageForOption:(NSString*)option {
}
- (NSString*)getStringForOption:(NSString*)option alias:(NSString*)alias {
- NSString* cachedValue =
- [self cachedValueOfClass:NSString.class forKey:option];
- if (cachedValue) {
+ if (NSString* cachedValue =
+ [self cachedValueOfClass:NSString.class forKey:option]) {
return cachedValue;
}
const char* value =
_isOpen ? rime_get_api()->config_get_cstring(&_config, option.UTF8String)
: NULL;
- if (value) {
+ if (value != NULL) {
NSString* string = [@(value)
stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
[_cache setObject:string forKey:option];
@@ -402,7 +392,7 @@ - (NSString*)getStringForOption:(NSString*)option alias:(NSString*)alias {
value = _isOpen ? rime_get_api()->config_get_cstring(&_config,
aliasOption.UTF8String)
: NULL;
- if (value) {
+ if (value != NULL) {
NSString* string = [@(value)
stringByTrimmingCharactersInSet:NSCharacterSet
.whitespaceCharacterSet];
@@ -414,20 +404,20 @@ - (NSString*)getStringForOption:(NSString*)option alias:(NSString*)alias {
}
- (NSColor*)getColorForOption:(NSString*)option alias:(NSString*)alias {
- NSColor* cachedValue = [self cachedValueOfClass:NSColor.class forKey:option];
- if (cachedValue) {
+ if (NSColor* cachedValue =
+ [self cachedValueOfClass:NSColor.class forKey:option]) {
return cachedValue;
}
- NSColor* color = [self colorFromString:[self getStringForOption:option]];
- if (color) {
+ if (NSColor* color =
+ [self colorFromString:[self getStringForOption:option]]) {
[_cache setObject:color forKey:option];
return color;
}
if (alias != nil) {
NSString* aliasOption = [option.stringByDeletingLastPathComponent
stringByAppendingPathComponent:alias.lastPathComponent];
- color = [self colorFromString:[self getStringForOption:aliasOption]];
- if (color) {
+ if (NSColor* color =
+ [self colorFromString:[self getStringForOption:aliasOption]]) {
[_cache setObject:color forKey:option];
return color;
}
@@ -436,20 +426,19 @@ - (NSColor*)getColorForOption:(NSString*)option alias:(NSString*)alias {
}
- (NSImage*)getImageForOption:(NSString*)option alias:(NSString*)alias {
- NSImage* cachedValue = [self cachedValueOfClass:NSImage.class forKey:option];
- if (cachedValue) {
+ if (NSImage* cachedValue =
+ [self cachedValueOfClass:NSImage.class forKey:option]) {
return cachedValue;
}
- NSImage* image = [self imageFromFile:[self getStringForOption:option]];
- if (image) {
+ if (NSImage* image = [self imageFromFile:[self getStringForOption:option]]) {
[_cache setObject:image forKey:option];
return image;
}
if (alias != nil) {
NSString* aliasOption = [option.stringByDeletingLastPathComponent
stringByAppendingPathComponent:alias.lastPathComponent];
- image = [self imageFromFile:[self getStringForOption:aliasOption]];
- if (image) {
+ if (NSImage* image =
+ [self imageFromFile:[self getStringForOption:aliasOption]]) {
[_cache setObject:image forKey:option];
return image;
}
@@ -515,7 +504,7 @@ - (NSUInteger)getListSizeForOption:(NSString*)option {
- (SquirrelOptionSwitcher*)getOptionSwitcher {
RimeConfigIterator switchIter;
if (!rime_get_api()->config_begin_list(&switchIter, &_config, "switches")) {
- return nil;
+ return [SquirrelOptionSwitcher.alloc initWithSchemaId:_schemaId];
}
NSMutableDictionary* switcher =
NSMutableDictionary.alloc.init;
@@ -527,10 +516,9 @@ - (SquirrelOptionSwitcher*)getOptionSwitcher {
while (rime_get_api()->config_next(&switchIter)) {
int reset = [self
getIntForOption:[@(switchIter.path) stringByAppendingString:@"/reset"]];
- NSString* name =
- [self getStringForOption:[@(switchIter.path)
- stringByAppendingString:@"/name"]];
- if (name) {
+ if (NSString* name =
+ [self getStringForOption:[@(switchIter.path)
+ stringByAppendingString:@"/name"]]) {
if ([self hasSection:[@"style/!" stringByAppendingString:name]] ||
[self hasSection:[@"style/" stringByAppendingString:name]]) {
switcher[name] = reset ? name : [@"!" stringByAppendingString:name];
@@ -605,10 +593,9 @@ - (SquirrelAppOptions*)getAppOptions:(NSString*)appName {
while (rime_get_api()->config_next(&iterator)) {
// NSLog(@"DEBUG option[%d]: %s (%s)", iterator.index, iterator.key,
// iterator.path);
- NSNumber *value = [self getOptionalBoolForOption:@(iterator.path)] ? :
- [self getOptionalIntForOption:@(iterator.path)] ? :
- [self getOptionalDoubleForOption:@(iterator.path)];
- if (value) {
+ if (NSNumber *value = [self getOptionalBoolForOption:@(iterator.path)] ? :
+ [self getOptionalIntForOption:@(iterator.path)] ? :
+ [self getOptionalDoubleForOption:@(iterator.path)]) {
appOptions[@(iterator.key)] = value;
}
}
@@ -619,17 +606,16 @@ - (SquirrelAppOptions*)getAppOptions:(NSString*)appName {
#pragma mark - Private methods
- (id)cachedValueOfClass:(Class)aClass forKey:(NSString*)key {
- id value = [_cache objectForKey:key];
- if ([value isMemberOfClass:aClass]) {
+ if (id value = [_cache objectForKey:key]; [value isMemberOfClass:aClass]) {
return value;
}
return nil;
}
- (NSNumber*)cachedValueOfObjCType:(const char*)type forKey:(NSString*)key {
- id value = [_cache objectForKey:key];
- if ([value isMemberOfClass:NSNumber.class] &&
- !strcmp([value objCType], type)) {
+ if (id value = [_cache objectForKey:key];
+ [value isMemberOfClass:NSNumber.class] &&
+ strcmp([value objCType], type) == 0) {
return value;
}
return nil;
@@ -641,8 +627,7 @@ - (NSColor*)colorFromString:(NSString*)string {
return nil;
}
NSScanner* hexScanner = [NSScanner scannerWithString:string];
- UInt hex = 0x0;
- if ([hexScanner scanHexInt:&hex] && hexScanner.atEnd) {
+ if (UInt hex = 0x0; [hexScanner scanHexInt:&hex] && hexScanner.atEnd) {
UInt r = hex % 0x100;
UInt g = hex / 0x100 % 0x100;
UInt b = hex / 0x10000 % 0x100;
diff --git a/SquirrelInputController.hh b/SquirrelInputController.hh
index 7ba40c994..d9749e75f 100644
--- a/SquirrelInputController.hh
+++ b/SquirrelInputController.hh
@@ -1,4 +1,3 @@
-#import
#import
@interface SquirrelInputController : IMKInputController
@@ -43,9 +42,7 @@ typedef NS_ENUM(NSUInteger, SquirrelIndex) {
NSMutableArray* candidateComments;
- (void)moveCursor:(NSUInteger)cursorPosition
- toPosition:(NSUInteger)targetPosition
- inlinePreedit:(BOOL)inlinePreedit
- inlineCandidate:(BOOL)inlineCandidate __attribute__((objc_direct));
+ toPosition:(NSUInteger)targetPosition __attribute__((objc_direct));
- (void)performAction:(SquirrelAction)action
onIndex:(SquirrelIndex)index __attribute__((objc_direct));
diff --git a/SquirrelInputController.mm b/SquirrelInputController.mm
index 8afa82397..89ba03f9c 100644
--- a/SquirrelInputController.mm
+++ b/SquirrelInputController.mm
@@ -6,7 +6,6 @@
#import "macos_keycode.hh"
#import
#import
-#import
#import
#import
@@ -30,7 +29,8 @@ @implementation SquirrelInputController {
BOOL _inlineCandidate;
BOOL _goodOldCapsLock;
BOOL _showingSwitcherMenu;
- // app-specific bug fix
+ // app-specific options and bug fix
+ SquirrelAppOptions* _appOptions;
BOOL _inlinePlaceholder;
BOOL _panellessCommitFix;
int _inlineOffset;
@@ -77,9 +77,9 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender {
BOOL handled = NO;
@autoreleasepool {
- if (!_session || !rime_get_api()->find_session(_session)) {
+ if (_session == 0 || !rime_get_api()->find_session(_session)) {
[self createSession];
- if (!_session) {
+ if (_session == 0) {
return NO;
}
}
@@ -194,7 +194,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender {
// sender, modifiers, keyCode, keyChars);
// translate mac keydown events to rime keyevents
int rime_keycode = rime_keycode_from_mac_keycode(keyCode);
- if (!rime_keycode) {
+ if (rime_keycode == 0) {
NSString* keyChars = ((modifiers & NSEventModifierFlagShift) &&
!(modifiers & (NSEventModifierFlagControl |
NSEventModifierFlagOption)))
@@ -211,7 +211,7 @@ - (BOOL)handleEvent:(NSEvent*)event client:(id)sender {
// Navigations, F1..F19)
rime_modifiers ^= kHyperMask;
}
- if (rime_keycode) {
+ if (rime_keycode != 0) {
if ((handled = [self processKey:rime_keycode
modifiers:rime_modifiers])) {
[self rimeUpdate];
@@ -265,10 +265,7 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index
} else if (point.x < head.x || index <= 0) {
[self performAction:kPROCESS onIndex:kHomeKey];
} else {
- [self moveCursor:_inlineCaretPos
- toPosition:index
- inlinePreedit:_inlinePreedit
- inlineCandidate:_inlineCandidate];
+ [self moveCursor:_inlineCaretPos toPosition:index];
}
return YES;
}
@@ -314,13 +311,13 @@ - (BOOL)processKey:(int)rime_keycode
if (newIndex != NSNotFound) {
if (!panel.locked && !panel.expanded &&
rime_keycode == (is_vertical ? XK_Left : XK_Down)) {
- [panel setExpanded:YES];
+ panel.expanded = YES;
}
rime_get_api()->highlight_candidate(_session, newIndex);
return YES;
} else if (!panel.locked && panel.expanded && panel.sectionNum == 0 &&
rime_keycode == (is_vertical ? XK_Right : XK_Up)) {
- [panel setExpanded:NO];
+ panel.expanded = NO;
return YES;
}
}
@@ -368,12 +365,10 @@ - (BOOL)processKey:(int)rime_keycode
}
- (void)moveCursor:(NSUInteger)cursorPosition
- toPosition:(NSUInteger)targetPosition
- inlinePreedit:(BOOL)inlinePreedit
- inlineCandidate:(BOOL)inlineCandidate __attribute__((objc_direct)) {
+ toPosition:(NSUInteger)targetPosition __attribute__((objc_direct)) {
BOOL vertical = NSApp.squirrelAppDelegate.panel.vertical;
@autoreleasepool {
- NSString* composition = !inlinePreedit && !inlineCandidate
+ NSString* composition = !_inlinePreedit && !_inlineCandidate
? _composedString
: _preeditString.string;
RIME_STRUCT(RimeContext, ctx);
@@ -388,14 +383,14 @@ - (void)moveCursor:(NSUInteger)cursorPosition
rime_get_api()->process_key(_session, vertical ? XK_Up : XK_Left,
kControlMask);
rime_get_api()->get_context(_session, &ctx);
- if (inlineCandidate) {
+ if (_inlineCandidate) {
size_t length =
ctx.composition.cursor_pos < ctx.composition.sel_end
? (size_t)ctx.composition.cursor_pos
: strlen(ctx.commit_text_preview) -
- (inlinePreedit ? 0
- : (size_t)(ctx.composition.cursor_pos -
- ctx.composition.sel_end));
+ (_inlinePreedit ? 0
+ : (size_t)(ctx.composition.cursor_pos -
+ ctx.composition.sel_end));
prefix = [[NSString.alloc initWithBytes:ctx.commit_text_preview
length:(NSUInteger)length
encoding:NSUTF8StringEncoding]
@@ -423,7 +418,7 @@ - (void)moveCursor:(NSUInteger)cursorPosition
kControlMask);
rime_get_api()->get_context(_session, &ctx);
suffix = [@(ctx.composition.preedit + ctx.composition.cursor_pos +
- (!inlinePreedit && !inlineCandidate ? 3 : 0))
+ (!_inlinePreedit && !_inlineCandidate ? 3 : 0))
stringByReplacingOccurrencesOfString:@" "
withString:@""];
rime_get_api()->free_context(&ctx);
@@ -465,7 +460,7 @@ - (void)performAction:(SquirrelAction)action
- (void)onChordTimer:(NSTimer*)timer {
// chord release triggered by timer
int processed_keys = 0;
- if (_chordKeyCount && _session) {
+ if (_chordKeyCount > 0 && _session != 0) {
// simulate key-ups
for (int i = 0; i < _chordKeyCount; ++i) {
if (rime_get_api()->process_key(_session, _chordKeyCodes[i],
@@ -531,7 +526,8 @@ - (NSUInteger)recognizedEvents:(id)sender {
Bool state) {
RimeStringSlice short_label =
rime_get_api()->get_state_label_abbreviated(session, option, state, True);
- if (short_label.str && short_label.length >= strlen(short_label.str)) {
+ if (short_label.str != NULL &&
+ short_label.length >= strlen(short_label.str)) {
return @(short_label.str);
} else {
RimeStringSlice long_label = rime_get_api()->get_state_label_abbreviated(
@@ -544,25 +540,22 @@ - (NSUInteger)recognizedEvents:(id)sender {
- (void)showInitialStatus __attribute__((objc_direct)) {
RIME_STRUCT(RimeStatus, status);
- if (_session && rime_get_api()->get_status(_session, &status)) {
+ if (_session != 0 && rime_get_api()->get_status(_session, &status)) {
_schemaId = @(status.schema_id);
NSString* schemaName =
status.schema_name ? @(status.schema_name) : @(status.schema_id);
NSMutableArray* options =
[NSMutableArray.alloc initWithCapacity:3];
- NSString* asciiMode =
- getOptionLabel(_session, "ascii_mode", status.is_ascii_mode);
- if (asciiMode) {
+ if (NSString* asciiMode =
+ getOptionLabel(_session, "ascii_mode", status.is_ascii_mode)) {
[options addObject:asciiMode];
}
- NSString* fullShape =
- getOptionLabel(_session, "full_shape", status.is_full_shape);
- if (fullShape) {
+ if (NSString* fullShape =
+ getOptionLabel(_session, "full_shape", status.is_full_shape)) {
[options addObject:fullShape];
}
- NSString* asciiPunct =
- getOptionLabel(_session, "ascii_punct", status.is_ascii_punct);
- if (asciiPunct) {
+ if (NSString* asciiPunct =
+ getOptionLabel(_session, "ascii_punct", status.is_ascii_punct)) {
[options addObject:asciiPunct];
}
rime_get_api()->free_status(&status);
@@ -602,7 +595,7 @@ - (void)activateServer:(id)sender {
keyboardLayout =
[@"com.apple.keylayout." stringByAppendingString:keyboardLayout];
}
- if (keyboardLayout) {
+ if (keyboardLayout != nil) {
[sender overrideKeyboardWithKeyboardNamed:keyboardLayout];
}
@@ -631,10 +624,10 @@ - (instancetype)initWithServer:(IMKServer*)server
delegate:(id)delegate
client:(id)inputClient {
// NSLog(@"initWithServer:delegate:client:");
- self = [super initWithServer:server delegate:delegate client:inputClient];
- if (self) {
+ if (self = [super initWithServer:server
+ delegate:delegate
+ client:inputClient]) {
[self createSession];
- self.delegate = self;
_candidateTexts = NSMutableArray.alloc.init;
_candidateComments = NSMutableArray.alloc.init;
}
@@ -664,7 +657,7 @@ - (void)deactivateServer:(id)sender {
- (void)commitComposition:(id)sender {
// NSLog(@"commitComposition:");
[self commitString:[self composedString:sender]];
- if (_session) {
+ if (_session != 0) {
rime_get_api()->clear_composition(_session);
}
[self hidePalettes];
@@ -750,7 +743,7 @@ - (NSRange)replacementRange {
- (void)commitString:(id)string {
// NSLog(@"commitString:");
- if (string) {
+ if (string != nil) {
[self.client insertText:string
replacementRange:NSMakeRange(NSNotFound, NSNotFound)];
}
@@ -760,7 +753,7 @@ - (void)commitString:(id)string {
- (void)cancelComposition {
[self commitString:[self originalString:self.client]];
[self hidePalettes];
- if (_session) {
+ if (_session != 0) {
rime_get_api()->clear_composition(_session);
}
}
@@ -786,8 +779,8 @@ - (void)showPreeditString:(NSString*)preedit
selRange:(NSRange)range
caretPos:(NSUInteger)pos __attribute__((objc_direct)) {
// NSLog(@"showPreeditString: '%@'", preedit);
- if ([preedit isEqualToString:_preeditString.string] &&
- NSEqualRanges(range, _inlineSelRange) && pos == _inlineCaretPos) {
+ if (pos == _inlineCaretPos && NSEqualRanges(range, _inlineSelRange) &&
+ [preedit isEqualToString:_preeditString.string]) {
return;
}
_inlineSelRange = range;
@@ -909,20 +902,11 @@ - (void)createSession __attribute__((objc_direct)) {
// NSLog(@"createSession: %@", app);
_session = rime_get_api()->create_session();
_schemaId = nil;
- if (_session) {
- SquirrelAppOptions* appOptions =
- [NSApp.squirrelAppDelegate.config getAppOptions:app];
- for (NSString* key in appOptions) {
- NSNumber* number = appOptions[key];
- if (strcmp(number.objCType, @encode(BOOL)) == 0) {
- Bool value = number.boolValue;
- // NSLog(@"set app option: %@ = %d", key, value);
- rime_get_api()->set_option(_session, key.UTF8String, value);
- }
- }
- _panellessCommitFix = appOptions[@"panelless_commit_fix"].boolValue;
- _inlinePlaceholder = appOptions[@"inline_placeholder"].boolValue;
- _inlineOffset = appOptions[@"inline_offset"].intValue;
+ if (_session != 0) {
+ _appOptions = [NSApp.squirrelAppDelegate.config getAppOptions:app];
+ _panellessCommitFix = _appOptions[@"panelless_commit_fix"].boolValue;
+ _inlinePlaceholder = _appOptions[@"inline_placeholder"].boolValue;
+ _inlineOffset = _appOptions[@"inline_offset"].intValue;
if ([app isEqualToString:_currentApp] && _asciiMode >= 0) {
rime_get_api()->set_option(_session, "ascii_mode", _asciiMode);
}
@@ -934,7 +918,7 @@ - (void)createSession __attribute__((objc_direct)) {
- (void)destroySession __attribute__((objc_direct)) {
// NSLog(@"destroySession:");
- if (_session) {
+ if (_session != 0) {
rime_get_api()->destroy_session(_session);
_session = 0;
}
@@ -967,12 +951,12 @@ static inline NSUInteger UTF8LengthToUTF16Length(const char* string,
.length;
}
-static inline NSUInteger fmin(NSUInteger int_a, NSUInteger int_b) {
- return int_a < int_b ? int_a : int_b;
+static inline NSUInteger fmin(NSUInteger x, NSUInteger y) {
+ return x < y ? x : y;
}
-static inline NSUInteger fmax(NSUInteger int_a, NSUInteger int_b) {
- return int_a < int_b ? int_b : int_a;
+static inline NSUInteger fmax(NSUInteger x, NSUInteger y) {
+ return x < y ? y : x;
}
- (void)rimeUpdate __attribute__((objc_direct)) {
@@ -984,7 +968,8 @@ - (void)rimeUpdate __attribute__((objc_direct)) {
RIME_STRUCT(RimeStatus, status);
if (rime_get_api()->get_status(_session, &status)) {
// enable schema specific ui style
- if (!_schemaId || strcmp(_schemaId.UTF8String, status.schema_id)) {
+ if (_schemaId == nil ||
+ strcmp(_schemaId.UTF8String, status.schema_id) != 0) {
_schemaId = @(status.schema_id);
_showingSwitcherMenu = (BOOL)rime_get_api()->get_option(_session, "dumb");
if (!_showingSwitcherMenu) {
@@ -992,11 +977,9 @@ - (void)rimeUpdate __attribute__((objc_direct)) {
[NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId
withRimeSession:_session];
// inline preedit
- _inlinePreedit = (panel.inlinePreedit &&
- !rime_get_api()->get_option(_session, "no_inline")) ||
- rime_get_api()->get_option(_session, "inline");
- _inlineCandidate = panel.inlineCandidate &&
- !rime_get_api()->get_option(_session, "no_inline");
+ _inlinePreedit = (panel.inlinePreedit && !_appOptions[@"no_inline"]) ||
+ _appOptions[@"inline"];
+ _inlineCandidate = panel.inlineCandidate && !_appOptions[@"no_inline"];
// if not inline, embed soft cursor in preedit string
rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit);
} else {
@@ -1021,15 +1004,13 @@ - (void)rimeUpdate __attribute__((objc_direct)) {
_originalString = originalString;
// update composed string
- if (!preedit || _showingSwitcherMenu) {
+ if (preedit == NULL || _showingSwitcherMenu) {
_composedString = @"";
} else if (!_inlinePreedit) { // remove soft cursor
- size_t cursorPos =
- (size_t)ctx.composition.cursor_pos -
- (ctx.composition.cursor_pos < ctx.composition.sel_end ? 3 : 0);
char composed[strlen(preedit) - 2];
- strlcpy(composed, preedit, cursorPos + 1);
- strlcat(composed, preedit + cursorPos + 3, strlen(preedit) - 2);
+ strlcpy(composed, preedit, (size_t)ctx.composition.cursor_pos + 1);
+ strlcat(composed, preedit + ctx.composition.cursor_pos + 3,
+ strlen(preedit) - 2);
_composedString = @(composed);
} else {
_composedString = @(preedit);
@@ -1051,7 +1032,8 @@ - (void)rimeUpdate __attribute__((objc_direct)) {
BOOL finalPage = (BOOL)ctx.menu.is_last_page;
NSRange selRange = NSMakeRange(start, end - start);
- didCompose |= !NSEqualRanges(_selRange, selRange) && pageNum == 0;
+ didCompose |= !NSEqualRanges(_selRange, selRange) &&
+ highlightedIndex == 0 && pageNum == 0;
_selRange = selRange;
// update expander and section status in tabular layout;
// already processed the action if _currentIndex == NSNotFound
@@ -1143,9 +1125,9 @@ - (void)rimeUpdate __attribute__((objc_direct)) {
caretPos:caretPos];
}
} else {
- if (_inlinePlaceholder && preedit) {
+ if (_inlinePlaceholder && preedit != NULL) {
[self showPlaceholder:kFullWidthSpace];
- } else if (!didCommit || preedit) {
+ } else if (!didCommit || preedit != NULL) {
[self showPreeditString:@"" selRange:NSMakeRange(0, 0) caretPos:0];
}
}
@@ -1220,11 +1202,12 @@ - (void)updateCandidate:(RimeCandidate*)candidate
return;
}
if (index == _candidateTexts.count ||
- strcmp(candidate->text, _candidateTexts[index].UTF8String)) {
+ strcmp(candidate->text, _candidateTexts[index].UTF8String) != 0) {
_candidateTexts[index] = @(candidate->text);
}
if (index == _candidateComments.count ||
- strcmp(candidate->comment ?: "", _candidateComments[index].UTF8String)) {
+ strcmp(candidate->comment ?: "", _candidateComments[index].UTF8String) !=
+ 0) {
_candidateComments[index] = @(candidate->comment ?: "");
}
}
diff --git a/SquirrelPanel.hh b/SquirrelPanel.hh
index abb9e062a..78514198e 100644
--- a/SquirrelPanel.hh
+++ b/SquirrelPanel.hh
@@ -1,4 +1,3 @@
-#import
#import "SquirrelInputController.hh"
@class SquirrelConfig;
@@ -40,7 +39,7 @@
statusShort:(NSString* _Nullable)messageShort
__attribute__((objc_direct));
// display
-- (void)showPreedit:(NSString* _Nullable)preeditString
+- (void)showPreedit:(NSString* _Nullable)preedit
selRange:(NSRange)selRange
caretPos:(NSUInteger)caretPos
candidateIndices:(NSRange)indexRange
diff --git a/SquirrelPanel.mm b/SquirrelPanel.mm
index 833f0828c..203ebfa9c 100644
--- a/SquirrelPanel.mm
+++ b/SquirrelPanel.mm
@@ -12,9 +12,16 @@
static const CGFloat kDefaultFontSize = 24;
static const CGFloat kOffsetGap = 5;
+template
+static inline T clamp(T x, T min, T max) {
+ const auto y = x < min ? min : x;
+ return y > max ? max : y;
+}
+
+__attribute__((objc_direct_members))
@interface NSBezierPath (BezierPathQuartzUtilities)
-@property(nonatomic, readonly) CGPathRef quartzPath;
+@property(nonatomic, readonly, nullable) CGPathRef quartzPath;
@end
@@ -27,8 +34,7 @@ - (CGPathRef)quartzPath {
// Need to begin a path here.
CGPathRef immutablePath = NULL;
// Then draw the path elements.
- NSInteger numElements = self.elementCount;
- if (numElements > 0) {
+ if (NSInteger numElements = self.elementCount; numElements > 0) {
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
for (NSInteger i = 0; i < numElements; i++) {
@@ -80,7 +86,7 @@ - (void)superscriptionRange:(NSRange)range {
NSFontAttributeName : font,
(id)kCTBaselineClassAttributeName :
(id)kCTBaselineClassIdeographicCentered,
- NSSuperscriptAttributeName : @(1)
+ NSSuperscriptAttributeName : @1
}
range:subRange];
}];
@@ -101,7 +107,7 @@ - (void)subscriptionRange:(NSRange)range {
NSFontAttributeName : font,
(id)kCTBaselineClassAttributeName :
(id)kCTBaselineClassIdeographicCentered,
- NSSuperscriptAttributeName : @(-1)
+ NSSuperscriptAttributeName : @-1
}
range:subRange];
}];
@@ -208,9 +214,9 @@ - (CGFloat)annotateRubyInRange:(NSRange)range
.location,
1)];
} else {
- // base string must use only one font so that all fall
- // within one glyph run and the ruby annotation is
- // aligned with no duplicates
+ /* 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];
@@ -287,16 +293,20 @@ - (NSAttributedString*)attributedStringHorizontalInVerticalForms {
imageWithSize:NSMakeSize(height, width)
flipped:YES
drawingHandler:^BOOL(NSRect dstRect) {
- CGContextRef context = NSGraphicsContext.currentContext.CGContext;
- CGContextSaveGState(context);
- CGContextTranslateCTM(context, NSWidth(dstRect) * 0.5,
- NSHeight(dstRect) * 0.5);
- CGContextRotateCTM(context, -M_PI_2);
+ [NSGraphicsContext saveGraphicsState];
+ NSGraphicsContext.currentContext.shouldAntialias = YES;
+ NSGraphicsContext.currentContext.imageInterpolation =
+ NSImageInterpolationHigh;
+ NSAffineTransform* transform = NSAffineTransform.transform;
+ [transform translateXBy:NSWidth(dstRect) * 0.5
+ yBy:NSHeight(dstRect) * 0.5];
+ [transform rotateByDegrees:-90.0];
+ [transform concat];
CGPoint origin =
CGPointMake(-self.size.width / width * NSHeight(dstRect) * 0.5,
-NSWidth(dstRect) * 0.5);
[self drawAtPoint:origin];
- CGContextRestoreGState(context);
+ [NSGraphicsContext restoreGraphicsState];
return YES;
}];
image.resizingMode = NSImageResizingModeStretch;
@@ -336,8 +346,17 @@ + (NSColorSpace*)labColorSpace {
@end // NSColorSpace (labColorSpace)
__attribute__((objc_direct_members))
-@implementation
-NSColor(semanticColors)
+@interface NSColor (semanticColors)
+
+@property(nonatomic, strong, readonly, nonnull, class)
+ NSColor* secondaryTextColor;
+@property(nonatomic, strong, readonly, nonnull, class) NSColor* accentColor;
+@property(nonatomic, strong, readonly, nonnull) NSColor* hooverColor;
+@property(nonatomic, strong, readonly, nonnull) NSColor* disabledColor;
+
+@end
+
+@implementation NSColor (semanticColors)
+ (NSColor*)secondaryTextColor {
if (@available(macOS 10.10, *)) {
@@ -384,97 +403,90 @@ - (NSColor*)disabledColor {
__attribute__((objc_direct_members))
@interface NSColor (NSColorWithLabColorSpace)
-@property(nonatomic, readonly) CGFloat luminanceComponent;
-@property(nonatomic, readonly) CGFloat aGnRdComponent;
-@property(nonatomic, readonly) CGFloat bBuYlComponent;
+typedef NS_CLOSED_ENUM(NSInteger, ColorInversionExtent) {
+ kStandardColorInversion = 0,
+ kAugmentedColorInversion = 1,
+ kModerateColorInversion = -1
+};
+
+@property(nonatomic, readonly) CGFloat lStarComponent; // Luminance
+@property(nonatomic, readonly) CGFloat aStarComponent; // Green-Red
+@property(nonatomic, readonly) CGFloat bStarComponent; // Blue-Yellow
@end
@implementation NSColor (NSColorWithLabColorSpace)
-typedef NS_ENUM(NSInteger, ColorInversionExtent) {
- kDefaultColorInversion = 0,
- kAugmentedColorInversion = 1,
- kModerateColorInversion = -1
-};
-
-+ (NSColor*)colorWithLabLuminance:(CGFloat)luminance
- aGnRd:(CGFloat)aGnRd
- bBuYl:(CGFloat)bBuYl
- alpha:(CGFloat)alpha {
++ (NSColor*)colorWithLabLStar:(CGFloat)lStar
+ aStar:(CGFloat)aStar
+ bStar:(CGFloat)bStar
+ alpha:(CGFloat)alpha {
CGFloat components[4];
- components[0] = fmax(fmin(luminance, 100.0), 0.0);
- components[1] = fmax(fmin(aGnRd, 127.0), -127.0);
- components[2] = fmax(fmin(bBuYl, 127.0), -127.0);
- components[3] = fmax(fmin(alpha, 1.0), 0.0);
+ components[0] = clamp(lStar, 0.0, 100.0);
+ components[1] = clamp(aStar, -127.0, 127.0);
+ components[2] = clamp(bStar, -127.0, 127.0);
+ components[3] = clamp(alpha, 0.0, 1.0);
return [NSColor colorWithColorSpace:NSColorSpace.labColorSpace
components:components
count:4];
}
-- (void)getLuminance:(CGFloat*)luminance
- aGnRd:(CGFloat*)aGnRd
- bBuYl:(CGFloat*)bBuYl
- alpha:(CGFloat*)alpha {
- static CGFloat luminanceComponent, aGnRdComponent, bBuYlComponent,
- alphaComponent;
+- (void)getLStar:(CGFloat*)lStar
+ aStar:(CGFloat*)aStar
+ bStar:(CGFloat*)bStar
+ alpha:(CGFloat*)alpha {
+ static CGFloat components[4] = {0.0, 0.0, 0.0, 1.0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
- CGFloat components[4] = {0.0, 0.0, 0.0, 1.0};
[([self.colorSpace isEqualTo:NSColorSpace.labColorSpace]
? self
: [self colorUsingColorSpace:NSColorSpace.labColorSpace])
getComponents:components];
- luminanceComponent = components[0] / 100.0;
- aGnRdComponent = components[1] / 127.0;
- bBuYlComponent = components[2] / 127.0;
- alphaComponent = components[3];
+ components[0] /= 100.0;
+ components[1] /= 127.0;
+ components[2] /= 127.0;
});
- if (luminance != NULL)
- *luminance = luminanceComponent;
- if (aGnRd != NULL)
- *aGnRd = aGnRdComponent;
- if (bBuYl != NULL)
- *bBuYl = bBuYlComponent;
+ if (lStar != NULL)
+ *lStar = components[0];
+ if (aStar != NULL)
+ *aStar = components[1];
+ if (bStar != NULL)
+ *bStar = components[2];
if (alpha != NULL)
- *alpha = alphaComponent;
+ *alpha = components[3];
}
-- (CGFloat)luminanceComponent {
- CGFloat luminance;
- [self getLuminance:&luminance aGnRd:NULL bBuYl:NULL alpha:NULL];
- return luminance;
+- (CGFloat)lStarComponent {
+ CGFloat lStarComponent;
+ [self getLStar:&lStarComponent aStar:NULL bStar:NULL alpha:NULL];
+ return lStarComponent;
}
-- (CGFloat)aGnRdComponent {
- CGFloat aGnRdComponent;
- [self getLuminance:NULL aGnRd:&aGnRdComponent bBuYl:NULL alpha:NULL];
- return aGnRdComponent;
+- (CGFloat)aStarComponent {
+ CGFloat aStarComponent;
+ [self getLStar:NULL aStar:&aStarComponent bStar:NULL alpha:NULL];
+ return aStarComponent;
}
-- (CGFloat)bBuYlComponent {
- CGFloat bBuYlComponent;
- [self getLuminance:NULL aGnRd:NULL bBuYl:&bBuYlComponent alpha:NULL];
- return bBuYlComponent;
+- (CGFloat)bStarComponent {
+ CGFloat bStarComponent;
+ [self getLStar:NULL aStar:NULL bStar:&bStarComponent alpha:NULL];
+ return bStarComponent;
}
- (NSColor*)colorByInvertingLuminanceToExtent:(ColorInversionExtent)extent {
NSColor* labColor = [self colorUsingColorSpace:NSColorSpace.labColorSpace];
CGFloat components[4] = {0.0, 0.0, 0.0, 1.0};
[labColor getComponents:components];
- BOOL isDark = components[0] < 60;
switch (extent) {
case kAugmentedColorInversion:
- components[0] = isDark ? 100.0 - components[0] * 2.0 / 3.0
- : 150.0 - components[0] * 1.5;
+ components[0] = 100.0 - components[0];
break;
case kModerateColorInversion:
- components[0] =
- isDark ? 80.0 - components[0] / 3.0 : 135.0 - components[0] * 1.25;
+ components[0] = fma(components[0], -0.6, 80.0);
break;
- case kDefaultColorInversion:
- components[0] =
- isDark ? 90.0 - components[0] / 2.0 : 120.0 - components[0];
+ case kStandardColorInversion:
+ components[0] = fma(components[0], -0.8, 90.0);
break;
}
NSColor* invertedColor =
@@ -491,13 +503,13 @@ - (NSColor*)colorByInvertingLuminanceToExtent:(ColorInversionExtent)extent {
__attribute__((objc_direct_members))
@interface SquirrelTheme : NSObject
-typedef NS_ENUM(NSUInteger, SquirrelAppear) {
- defaultAppear = 0,
- lightAppear = 0,
- darkAppear = 1
+typedef NS_CLOSED_ENUM(BOOL, SquirrelAppearance) {
+ kDefaultAppearance = NO,
+ kLightAppearance = NO,
+ kDarkAppearance = YES
};
-typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) {
+typedef NS_CLOSED_ENUM(NSUInteger, SquirrelStatusMessageType) {
kStatusMessageTypeMixed = 0,
kStatusMessageTypeShort = 1,
kStatusMessageTypeLong = 2
@@ -523,15 +535,15 @@ typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) {
@property(nonatomic, strong, readonly, nullable) NSColor* borderColor;
@property(nonatomic, strong, readonly, nullable) NSImage* backImage;
+@property(nonatomic, readonly) NSSize borderInsets;
@property(nonatomic, readonly) CGFloat cornerRadius;
@property(nonatomic, readonly) CGFloat hilitedCornerRadius;
@property(nonatomic, readonly) CGFloat fullWidth;
@property(nonatomic, readonly) CGFloat linespace;
@property(nonatomic, readonly) CGFloat preeditLinespace;
@property(nonatomic, readonly) CGFloat opacity;
-@property(nonatomic, readonly) CGFloat translucency;
@property(nonatomic, readonly) CGFloat lineLength;
-@property(nonatomic, readonly) NSSize borderInsets;
+@property(nonatomic, readonly) float translucency;
@property(nonatomic, readonly) BOOL showPaging;
@property(nonatomic, readonly) BOOL rememberSize;
@property(nonatomic, readonly) BOOL tabular;
@@ -564,8 +576,6 @@ typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) {
NSParagraphStyle* truncatedParagraphStyle;
@property(nonatomic, strong, readonly, nonnull) NSAttributedString* separator;
-@property(nonatomic, strong, readonly, nonnull)
- NSAttributedString* fullWidthPlaceholder;
@property(nonatomic, strong, readonly, nonnull)
NSAttributedString* symbolDeleteFill;
@property(nonatomic, strong, readonly, nonnull)
@@ -599,22 +609,16 @@ typedef NS_ENUM(NSUInteger, SquirrelStatusMessageType) {
- (void)updateLabelsWithConfig:(SquirrelConfig* _Nonnull)config
directUpdate:(BOOL)update;
-
- (void)setSelectKeys:(NSString* _Nonnull)selectKeys
labels:(NSArray* _Nonnull)labels
directUpdate:(BOOL)update;
-
- (void)setCandidateFormat:(NSString* _Nonnull)candidateFormat;
-
- (void)setStatusMessageType:(NSString* _Nullable)type;
-
- (void)updateWithConfig:(SquirrelConfig* _Nonnull)config
styleOptions:(NSSet* _Nonnull)styleOptions
scriptVariant:(NSString* _Nonnull)scriptVariant
- forAppearance:(SquirrelAppear)appear;
-
+ forAppearance:(SquirrelAppearance)appearance;
- (void)setAnnotationHeight:(CGFloat)height;
-
- (void)setScriptVariant:(NSString* _Nonnull)scriptVariant;
@end
@@ -637,15 +641,14 @@ @implementation SquirrelTheme
NSMutableArray* validFontDescriptors =
[NSMutableArray.alloc initWithCapacity:fontNames.count];
for (NSString* fontName in fontNames) {
- NSFont* font = [NSFont
- fontWithName:[fontName
- stringByTrimmingCharactersInSet:
- NSCharacterSet.whitespaceAndNewlineCharacterSet]
- size:0.0];
- if (font != nil) {
- // If the font name is not valid, NSFontDescriptor will still create
- // something for us. However, when we draw the actual text, Squirrel will
- // crash if there is any font descriptor with invalid font name.
+ if (NSFont* font = [NSFont
+ fontWithName:[fontName stringByTrimmingCharactersInSet:
+ NSCharacterSet
+ .whitespaceAndNewlineCharacterSet]
+ size:0.0]) {
+ /* If the font name is not valid, NSFontDescriptor will still create
+ something for us. However, when we draw the actual text, Squirrel will
+ crash if there is any font descriptor with invalid font name. */
NSFontDescriptor* fontDescriptor = font.fontDescriptor;
NSFontDescriptor* UIFontDescriptor = [fontDescriptor
fontDescriptorWithSymbolicTraits:NSFontDescriptorTraitUIOptimized];
@@ -689,15 +692,14 @@ static CGFloat getLineHeight(NSFont* font, BOOL vertical) {
}
- (instancetype)init {
- self = [super init];
- if (self) {
+ if (self = [super init]) {
NSMutableParagraphStyle* candidateParagraphStyle =
NSMutableParagraphStyle.alloc.init;
candidateParagraphStyle.alignment = NSTextAlignmentLeft;
candidateParagraphStyle.lineBreakStrategy = NSLineBreakStrategyNone;
- // Use left-to-right marks to declare the default writing direction and
- // prevent strong right-to-left characters from setting the writing
- // direction in case the label are direction-less symbols
+ /* Use left-to-right marks to declare the default writing direction and
+ prevent strong right-to-left characters from setting the writing
+ direction in case the label are direction-less symbols */
candidateParagraphStyle.baseWritingDirection =
NSWritingDirectionLeftToRight;
NSMutableParagraphStyle* preeditParagraphStyle =
@@ -728,7 +730,7 @@ - (instancetype)init {
textAttrs[NSFontAttributeName] = userFont;
// Use left-to-right embedding to prevent right-to-left text from changing
// the layout of the candidate.
- textAttrs[NSWritingDirectionAttributeName] = @[ @(0) ];
+ textAttrs[NSWritingDirectionAttributeName] = @[ @0 ];
textAttrs[NSParagraphStyleAttributeName] = candidateParagraphStyle;
NSMutableDictionary* labelAttrs =
@@ -747,13 +749,14 @@ - (instancetype)init {
NSMutableDictionary.alloc.init;
preeditAttrs[NSForegroundColorAttributeName] = NSColor.textColor;
preeditAttrs[NSFontAttributeName] = userFont;
- preeditAttrs[NSLigatureAttributeName] = @(0);
+ preeditAttrs[NSLigatureAttributeName] = @0;
preeditAttrs[NSParagraphStyleAttributeName] = preeditParagraphStyle;
NSMutableDictionary* pagingAttrs =
NSMutableDictionary.alloc.init;
pagingAttrs[NSFontAttributeName] = monoDigitFont;
pagingAttrs[NSForegroundColorAttributeName] = NSColor.textColor;
+ pagingAttrs[NSParagraphStyleAttributeName] = pagingParagraphStyle;
NSMutableDictionary* statusAttrs =
commentAttrs.mutableCopy;
@@ -795,14 +798,11 @@ - (instancetype)init {
- (void)updateSeperatorAndSymbolAttrs {
NSMutableDictionary* sepAttrs =
_commentAttrs.mutableCopy;
- sepAttrs[NSVerticalGlyphFormAttributeName] = @(NO);
+ sepAttrs[NSVerticalGlyphFormAttributeName] = @NO;
_separator = [NSAttributedString.alloc
initWithString:_linear ? (_tabular ? @"\u3000\t\x1D" : @"\u3000\x1D")
: @"\n"
attributes:sepAttrs];
- _fullWidthPlaceholder =
- [NSAttributedString.alloc initWithString:kFullWidthSpace
- attributes:_commentAttrs];
// Symbols for function buttons
NSString* attmCharacter =
[NSString stringWithCharacters:(unichar[1]){NSAttachmentCharacter}
@@ -813,7 +813,7 @@ - (void)updateSeperatorAndSymbolAttrs {
NSMutableDictionary* attrsDeleteFill =
_preeditAttrs.mutableCopy;
attrsDeleteFill[NSAttachmentAttributeName] = attmDeleteFill;
- attrsDeleteFill[NSVerticalGlyphFormAttributeName] = @(NO);
+ attrsDeleteFill[NSVerticalGlyphFormAttributeName] = @NO;
_symbolDeleteFill = [NSAttributedString.alloc initWithString:attmCharacter
attributes:attrsDeleteFill];
@@ -822,7 +822,7 @@ - (void)updateSeperatorAndSymbolAttrs {
NSMutableDictionary* attrsDeleteStroke =
_preeditAttrs.mutableCopy;
attrsDeleteStroke[NSAttachmentAttributeName] = attmDeleteStroke;
- attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @(NO);
+ attrsDeleteStroke[NSVerticalGlyphFormAttributeName] = @NO;
_symbolDeleteStroke =
[NSAttributedString.alloc initWithString:attmCharacter
attributes:attrsDeleteStroke];
@@ -925,7 +925,7 @@ - (void)updateLabelsWithConfig:(SquirrelConfig*)config
addObjectsFromArray:[selectLabels
subarrayWithRange:NSMakeRange(0, menuSize)]];
}
- if (selectKeys) {
+ if (selectKeys != nil) {
if (selectLabels.count == 0) {
NSString* keyCaps = [selectKeys.uppercaseString
stringByApplyingTransform:NSStringTransformFullwidthToHalfwidth
@@ -969,7 +969,7 @@ - (void)setCandidateFormat:(NSString*)candidateFormat {
}
- (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
- NSMutableAttributedString* candTemplate;
+ NSMutableAttributedString* candidateTemplate;
if (!attrsOnly) {
// validate candidate format: must have enumerator '%c' before candidate
// '%@'
@@ -997,94 +997,93 @@ - (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
isSupersetOfSet:labelCharacters]) { // 01..9
if ((enumRange = [candidateFormat rangeOfString:@"%c\u20E3"
options:NSLiteralSearch])
- .length > 0) { // 1︎⃣..9︎⃣0︎⃣
+ .length > 0) { // 1︎⃣...9︎⃣0︎⃣
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
- stringWithFormat:@"%S", (const unichar[3]){
- [labels[i] characterAtIndex:0] -
- 0xFF10 + 0x0030,
- 0xFE0E, 0x20E3}];
+ stringWithFormat:@"%C\uFE0E\u20E3",
+ (unichar)([labels[i] characterAtIndex:0] -
+ 0xFF10 + 0x0030)];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"%c\u20DD"
options:NSLiteralSearch])
- .length > 0) { // ①..⑨⓪
+ .length > 0) { // ①...⑨⓪
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
- stringWithFormat:@"%S",
- (const unichar[1]){
- [labels[i] characterAtIndex:0] == 0xFF10
- ? 0x24EA
- : [labels[i] characterAtIndex:0] -
- 0xFF11 + 0x2460}];
+ stringWithFormat:@"%C",
+ (unichar)([labels[i] characterAtIndex:0] ==
+ 0xFF10
+ ? 0x24EA
+ : [labels[i] characterAtIndex:0] -
+ 0xFF11 + 0x2460)];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"(%c)"
options:NSLiteralSearch])
- .length > 0) { // ⑴..⑼⑽
+ .length > 0) { // ⑴...⑼⑽
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
- stringWithFormat:@"%S",
- (const unichar[1]){
- [labels[i] characterAtIndex:0] == 0xFF10
- ? 0x247D
- : [labels[i] characterAtIndex:0] -
- 0xFF11 + 0x2474}];
+ stringWithFormat:@"%C",
+ (unichar)([labels[i] characterAtIndex:0] ==
+ 0xFF10
+ ? 0x247D
+ : [labels[i] characterAtIndex:0] -
+ 0xFF11 + 0x2474)];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"%c."
options:NSLiteralSearch])
- .length > 0) { // ⒈..⒐🄀
+ .length > 0) { // ⒈...⒐🄀
for (NSUInteger i = 0; i < labels.count; ++i) {
- labels[i] = [NSString
- stringWithFormat:@"%S",
- (const unichar[2]){
- [labels[i] characterAtIndex:0] == 0xFF10
- ? 0xD83C
- : [labels[i] characterAtIndex:0] -
- 0xFF11 + 0x2488,
- [labels[i] characterAtIndex:0] == 0xFF10
- ? 0xDD00
- : 0x0}];
+ labels[i] =
+ [labels[i] characterAtIndex:0] == 0xFF10
+ ? @"\U0001F100"
+ : [NSString
+ stringWithFormat:@"%C",
+ (unichar)(
+ [labels[i] characterAtIndex:0] -
+ 0xFF11 + 0x2488)];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"%c,"
options:NSLiteralSearch])
- .length > 0) { // 🄂..🄊🄁
+ .length > 0) { // 🄂...🄊🄁
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
stringWithFormat:@"%S",
(const unichar[2]){
- 0xD83C, [labels[i] characterAtIndex:0] -
- 0xFF10 + 0xDD01}];
+ 0xD83C,
+ (unichar)([labels[i] characterAtIndex:0] -
+ 0xFF10 + 0xDD01)}];
}
}
} else if ([[NSCharacterSet characterSetWithRange:NSMakeRange(0xFF21, 26)]
isSupersetOfSet:labelCharacters]) { // A..Z
if ((enumRange = [candidateFormat rangeOfString:@"%c\u20DD"
options:NSLiteralSearch])
- .length > 0) { // Ⓐ..Ⓩ
+ .length > 0) { // Ⓐ...Ⓩ
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
- stringWithFormat:@"%S", (const unichar[1]){
- [labels[i] characterAtIndex:0] -
- 0xFF21 + 0x24B6}];
+ stringWithFormat:@"%C", (unichar)([labels[i] characterAtIndex:0] -
+ 0xFF21 + 0x24B6)];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"(%c)"
options:NSLiteralSearch])
- .length > 0) { // 🄐..🄩
+ .length > 0) { // 🄐...🄩
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
stringWithFormat:@"%S",
(const unichar[2]){
- 0xD83C, [labels[i] characterAtIndex:0] -
- 0xFF21 + 0xDD10}];
+ 0xD83C,
+ (unichar)([labels[i] characterAtIndex:0] -
+ 0xFF21 + 0xDD10)}];
}
} else if ((enumRange = [candidateFormat rangeOfString:@"%c\u20DE"
options:NSLiteralSearch])
- .length > 0) { // 🄰..🅉
+ .length > 0) { // 🄰...🅉
for (NSUInteger i = 0; i < labels.count; ++i) {
labels[i] = [NSString
stringWithFormat:@"%S",
(const unichar[2]){
- 0xD83C, [labels[i] characterAtIndex:0] -
- 0xFF21 + 0xDD30}];
+ 0xD83C,
+ (unichar)([labels[i] characterAtIndex:0] -
+ 0xFF21 + 0xDD30)}];
}
}
}
@@ -1092,10 +1091,10 @@ - (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
[candidateFormat replaceCharactersInRange:enumRange withString:@"%c"];
_labels = labels;
}
- candTemplate =
+ candidateTemplate =
[NSMutableAttributedString.alloc initWithString:candidateFormat];
} else {
- candTemplate = _candidateTemplate.mutableCopy;
+ candidateTemplate = _candidateTemplate.mutableCopy;
}
// make sure label font can render all label strings
NSString* labelString = [_labels componentsJoinedByString:@""];
@@ -1126,40 +1125,45 @@ - (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
}
NSRange textRange =
- [candTemplate.mutableString rangeOfString:@"%@" options:NSLiteralSearch];
+ [candidateTemplate.mutableString rangeOfString:@"%@"
+ options:NSLiteralSearch];
NSRange labelRange = NSMakeRange(0, textRange.location);
NSRange commentRange = NSMakeRange(
- NSMaxRange(textRange), candTemplate.length - NSMaxRange(textRange));
- [candTemplate setAttributes:_labelAttrs range:labelRange];
- [candTemplate setAttributes:_textAttrs range:textRange];
+ NSMaxRange(textRange), candidateTemplate.length - NSMaxRange(textRange));
+ [candidateTemplate setAttributes:_labelAttrs range:labelRange];
+ [candidateTemplate setAttributes:_textAttrs range:textRange];
if (commentRange.length > 0) {
- [candTemplate setAttributes:_commentAttrs range:commentRange];
+ [candidateTemplate setAttributes:_commentAttrs range:commentRange];
}
// parse markdown formats
if (!attrsOnly) {
- [candTemplate formatMarkDown];
+ [candidateTemplate formatMarkDown];
// add placeholder for comment '%s'
- textRange = [candTemplate.mutableString rangeOfString:@"%@"
- options:NSLiteralSearch];
+ textRange = [candidateTemplate.mutableString rangeOfString:@"%@"
+ options:NSLiteralSearch];
labelRange = NSMakeRange(0, textRange.location);
- commentRange = NSMakeRange(NSMaxRange(textRange),
- candTemplate.length - NSMaxRange(textRange));
+ commentRange =
+ NSMakeRange(NSMaxRange(textRange),
+ candidateTemplate.length - NSMaxRange(textRange));
if (commentRange.length > 0) {
- [candTemplate replaceCharactersInRange:commentRange
- withString:[kTipSpecifier
- stringByAppendingString:
- [candTemplate.mutableString
- substringWithRange:
- commentRange]]];
+ [candidateTemplate
+ replaceCharactersInRange:commentRange
+ withString:
+ [kTipSpecifier
+ stringByAppendingString:
+ [candidateTemplate.mutableString
+ substringWithRange:commentRange]]];
} else {
- [candTemplate appendAttributedString:[NSAttributedString.alloc
- initWithString:kTipSpecifier
- attributes:_commentAttrs]];
+ [candidateTemplate
+ appendAttributedString:[NSAttributedString.alloc
+ initWithString:kTipSpecifier
+ attributes:_commentAttrs]];
}
commentRange.length += kTipSpecifier.length;
if (!_linear) {
- [candTemplate replaceCharactersInRange:NSMakeRange(textRange.location, 0)
- withString:@"\t"];
+ [candidateTemplate
+ replaceCharactersInRange:NSMakeRange(textRange.location, 0)
+ withString:@"\t"];
labelRange.length += 1;
textRange.location += 1;
commentRange.location += 1;
@@ -1170,7 +1174,7 @@ - (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
_candidateParagraphStyle.mutableCopy;
if (!_linear) {
CGFloat indent = 0.0;
- NSAttributedString* labelFormat = [candTemplate
+ NSAttributedString* labelFormat = [candidateTemplate
attributedSubstringFromRange:NSMakeRange(0, labelRange.length - 1)];
for (NSString* label in _labels) {
NSMutableAttributedString* enumString = labelFormat.mutableCopy;
@@ -1212,27 +1216,29 @@ - (void)updateCandidateFormatForAttributesOnly:(BOOL)attrsOnly {
_commentAttrs = commentAttrs;
_labelAttrs = labelAttrs;
- [candTemplate addAttribute:NSParagraphStyleAttributeName
- value:candidateParagraphStyle
- range:NSMakeRange(0, candTemplate.length)];
- _candidateTemplate = candTemplate;
- NSMutableAttributedString* candHilitedTemplate = candTemplate.mutableCopy;
- [candHilitedTemplate addAttribute:NSForegroundColorAttributeName
- value:_hilitedLabelForeColor
- range:labelRange];
- [candHilitedTemplate addAttribute:NSForegroundColorAttributeName
- value:_hilitedTextForeColor
- range:textRange];
- [candHilitedTemplate addAttribute:NSForegroundColorAttributeName
- value:_hilitedCommentForeColor
- range:commentRange];
- _candidateHilitedTemplate = candHilitedTemplate;
+ [candidateTemplate addAttribute:NSParagraphStyleAttributeName
+ value:candidateParagraphStyle
+ range:NSMakeRange(0, candidateTemplate.length)];
+ _candidateTemplate = candidateTemplate;
+ NSMutableAttributedString* candidateHilitedTemplate =
+ candidateTemplate.mutableCopy;
+ [candidateHilitedTemplate addAttribute:NSForegroundColorAttributeName
+ value:_hilitedLabelForeColor
+ range:labelRange];
+ [candidateHilitedTemplate addAttribute:NSForegroundColorAttributeName
+ value:_hilitedTextForeColor
+ range:textRange];
+ [candidateHilitedTemplate addAttribute:NSForegroundColorAttributeName
+ value:_hilitedCommentForeColor
+ range:commentRange];
+ _candidateHilitedTemplate = candidateHilitedTemplate;
if (_tabular) {
- NSMutableAttributedString* candDimmedTemplate = candTemplate.mutableCopy;
- [candDimmedTemplate addAttribute:NSForegroundColorAttributeName
- value:_dimmedLabelForeColor
- range:labelRange];
- _candidateDimmedTemplate = candDimmedTemplate;
+ NSMutableAttributedString* candidateDimmedTemplate =
+ candidateTemplate.mutableCopy;
+ [candidateDimmedTemplate addAttribute:NSForegroundColorAttributeName
+ value:_dimmedLabelForeColor
+ range:labelRange];
+ _candidateDimmedTemplate = candidateDimmedTemplate;
}
}
@@ -1298,24 +1304,24 @@ static void updateTextOrientation(BOOL* isVertical,
}
// functions for post-retrieve processing
-static double inline positive(double param) {
+static inline double positive(double param) {
return param > 0.0 ? param : 0.0;
}
-static double inline pos_round(double param) {
+static inline double pos_round(double param) {
return param > 0.0 ? round(param) : 0.0;
}
-static double inline pos_ceil(double param) {
+static inline double pos_ceil(double param) {
return param > 0.0 ? ceil(param) : 0.0;
}
-static double inline clamp_uni(double param) {
+static inline double clamp_uni(double param) {
return param > 0.0 ? (param < 1.0 ? param : 1.0) : 0.0;
}
- (void)updateWithConfig:(SquirrelConfig*)config
styleOptions:(NSSet*)styleOptions
scriptVariant:(NSString*)scriptVariant
- forAppearance:(SquirrelAppear)appear {
- // INTERFACE
+ forAppearance:(SquirrelAppearance)appearance {
+ /*** INTERFACE ***/
BOOL linear = NO;
BOOL tabular = NO;
BOOL vertical = NO;
@@ -1332,7 +1338,7 @@ - (void)updateWithConfig:(SquirrelConfig*)config
[config getStringForOption:@"style/status_message_type"];
NSString* candidateFormat =
[config getStringForOption:@"style/candidate_format"];
- // TYPOGRAPHY
+ /*** TYPOGRAPHY ***/
NSString* fontName = [config getStringForOption:@"style/font_face"];
NSNumber* fontSize = [config getOptionalDoubleForOption:@"style/font_point"
applyConstraint:pos_round];
@@ -1373,7 +1379,7 @@ - (void)updateWithConfig:(SquirrelConfig*)config
[config getOptionalDoubleForOption:@"style/base_offset"];
NSNumber* lineLength =
[config getOptionalDoubleForOption:@"style/line_length"];
- // CHROMATICS
+ /*** CHROMATICS ***/
NSColor* backColor;
NSColor* borderColor;
NSColor* preeditBackColor;
@@ -1390,24 +1396,24 @@ - (void)updateWithConfig:(SquirrelConfig*)config
NSImage* backImage;
NSString* colorScheme;
- if (appear == darkAppear) {
+ if (appearance == kDarkAppearance) {
for (NSString* option in styleOptions) {
if ((colorScheme = [config
getStringForOption:
[NSString stringWithFormat:@"style/%@/color_scheme_dark",
- option]])) {
+ option]]) != nil) {
break;
}
}
colorScheme =
colorScheme ?: [config getStringForOption:@"style/color_scheme_dark"];
}
- if (!colorScheme) {
+ if (colorScheme == nil) {
for (NSString* option in styleOptions) {
if ((colorScheme = [config
getStringForOption:[NSString
stringWithFormat:@"style/%@/color_scheme",
- option]])) {
+ option]]) != nil) {
break;
}
}
@@ -1427,7 +1433,7 @@ - (void)updateWithConfig:(SquirrelConfig*)config
// get color scheme and then check possible overrides from styleSwitcher
for (NSString* prefix in configPrefixes) {
- // CHROMATICS override
+ /*** CHROMATICS override ***/
config.colorSpace =
[config
getStringForOption:[prefix stringByAppendingString:@"/color_space"]]
@@ -1492,9 +1498,10 @@ - (void)updateWithConfig:(SquirrelConfig*)config
getImageForOption:[prefix stringByAppendingString:@"/back_image"]]
?: backImage;
- // the following per-color-scheme configurations, if exist, will
- // override configurations with the same name under the global 'style'
- // section INTERFACE override
+ /* the following per-color-scheme configurations, if exist, will
+ override configurations with the same name under the global 'style'
+ section */
+ /*** INTERFACE override ***/
updateCandidateListLayout(&linear, &tabular, config, prefix);
updateTextOrientation(&vertical, config, prefix);
inlinePreedit =
@@ -1520,7 +1527,7 @@ - (void)updateWithConfig:(SquirrelConfig*)config
[config getStringForOption:
[prefix stringByAppendingString:@"/candidate_format"]]
?: candidateFormat;
- // TYPOGRAPHY override
+ /*** TYPOGRAPHY override ***/
fontName =
[config
getStringForOption:[prefix stringByAppendingString:@"/font_face"]]
@@ -1596,7 +1603,7 @@ - (void)updateWithConfig:(SquirrelConfig*)config
?: lineLength;
}
- // TYPOGRAPHY refinement
+ /*** TYPOGRAPHY refinement ***/
fontSize = fontSize ?: @(kDefaultFontSize);
labelFontSize = labelFontSize ?: fontSize;
commentFontSize = commentFontSize ?: fontSize;
@@ -1647,8 +1654,8 @@ - (void)updateWithConfig:(SquirrelConfig*)config
CGFloat fullWidth = ceil(
[kFullWidthSpace sizeWithAttributes:@{NSFontAttributeName : commentFont}]
.width);
- spacing = spacing ?: @(0.0);
- lineSpacing = lineSpacing ?: @(0.0);
+ spacing = spacing ?: @0;
+ lineSpacing = lineSpacing ?: @0;
NSMutableParagraphStyle* preeditParagraphStyle =
_preeditParagraphStyle.mutableCopy;
@@ -1659,8 +1666,6 @@ - (void)updateWithConfig:(SquirrelConfig*)config
NSMutableParagraphStyle* candidateParagraphStyle =
_candidateParagraphStyle.mutableCopy;
- candidateParagraphStyle.alignment =
- linear ? NSTextAlignmentNatural : NSTextAlignmentLeft;
candidateParagraphStyle.minimumLineHeight = lineHeight;
candidateParagraphStyle.maximumLineHeight = lineHeight;
candidateParagraphStyle.paragraphSpacingBefore =
@@ -1718,14 +1723,15 @@ - (void)updateWithConfig:(SquirrelConfig*)config
NSDictionary* baselineRefInfo = @{
(id)kCTBaselineReferenceFont : vertical ? refFont.verticalFont : refFont,
(id)kCTBaselineClassIdeographicCentered :
- @(vertical ? 0.0 : refFont.ascender * 0.5 + refFont.descender * 0.5),
- (id)kCTBaselineClassRoman :
- @(vertical ? -refFont.verticalFont.ascender * 0.5 -
- refFont.verticalFont.descender * 0.5
- : 0.0),
+ @(vertical ? 0.0 : (refFont.ascender + refFont.descender) * 0.5),
+ (id)kCTBaselineClassRoman : @(vertical ? -(refFont.verticalFont.ascender +
+ refFont.verticalFont.descender) *
+ 0.5
+ : 0.0),
(id)kCTBaselineClassIdeographicLow :
- @(vertical ? refFont.verticalFont.descender * 0.5 -
- refFont.verticalFont.ascender * 0.5
+ @(vertical ? (refFont.verticalFont.descender -
+ refFont.verticalFont.ascender) *
+ 0.5
: refFont.descender)
};
@@ -1776,28 +1782,28 @@ - (void)updateWithConfig:(SquirrelConfig*)config
statusAttrs[NSParagraphStyleAttributeName] = statusParagraphStyle;
labelAttrs[NSVerticalGlyphFormAttributeName] = @(vertical);
- pagingAttrs[NSVerticalGlyphFormAttributeName] = @(NO);
+ pagingAttrs[NSVerticalGlyphFormAttributeName] = @NO;
- // CHROMATICS refinement
- translucency = translucency ?: @(0.0);
+ /*** CHROMATICS refinement ***/
+ translucency = translucency ?: @0;
if (@available(macOS 10.14, *)) {
- if (translucency.doubleValue > 0.001 && !isNative && backColor != nil &&
- (appear == darkAppear ? backColor.luminanceComponent > 0.65
- : backColor.luminanceComponent < 0.55)) {
+ if (translucency.floatValue > 0.001f && !isNative && backColor != nil &&
+ (appearance == kDarkAppearance ? backColor.lStarComponent > 0.6
+ : backColor.lStarComponent < 0.4)) {
backColor =
- [backColor colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ [backColor colorByInvertingLuminanceToExtent:kStandardColorInversion];
borderColor = [borderColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
preeditBackColor = [preeditBackColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
preeditForeColor = [preeditForeColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
textForeColor = [textForeColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
commentForeColor = [commentForeColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
labelForeColor = [labelForeColor
- colorByInvertingLuminanceToExtent:kDefaultColorInversion];
+ colorByInvertingLuminanceToExtent:kStandardColorInversion];
hilitedPreeditBackColor = [hilitedPreeditBackColor
colorByInvertingLuminanceToExtent:kModerateColorInversion];
hilitedPreeditForeColor = [hilitedPreeditForeColor
@@ -1851,6 +1857,9 @@ - (void)updateWithConfig:(SquirrelConfig*)config
pagingAttrs[NSForegroundColorAttributeName] = preeditForeColor;
statusAttrs[NSForegroundColorAttributeName] = commentForeColor;
+ _borderInsets =
+ vertical ? NSMakeSize(borderHeight.doubleValue, borderWidth.doubleValue)
+ : NSMakeSize(borderWidth.doubleValue, borderHeight.doubleValue);
_cornerRadius = fmin(cornerRadius.doubleValue, lineHeight * 0.5);
_hilitedCornerRadius =
fmin(hilitedCornerRadius.doubleValue, lineHeight * 0.5);
@@ -1858,13 +1867,10 @@ - (void)updateWithConfig:(SquirrelConfig*)config
_linespace = lineSpacing.doubleValue;
_preeditLinespace = spacing.doubleValue;
_opacity = opacity ? opacity.doubleValue : 1.0;
- _translucency = translucency.doubleValue;
_lineLength = lineLength.doubleValue > 0.1
? fmax(ceil(lineLength.doubleValue), fullWidth * 5)
: 0.0;
- _borderInsets =
- vertical ? NSMakeSize(borderHeight.doubleValue, borderWidth.doubleValue)
- : NSMakeSize(borderWidth.doubleValue, borderHeight.doubleValue);
+ _translucency = translucency.floatValue;
_showPaging = showPaging.boolValue;
_rememberSize = rememberSize.boolValue;
_tabular = tabular;
@@ -1940,26 +1946,27 @@ - (void)setAnnotationHeight:(CGFloat)height {
_commentAttrs = commentAttrs;
_labelAttrs = labelAttrs;
- NSMutableAttributedString* candTemplate = _candidateTemplate.mutableCopy;
- [candTemplate addAttribute:NSParagraphStyleAttributeName
- value:candidateParagraphStyle
- range:NSMakeRange(0, candTemplate.length)];
- _candidateTemplate = candTemplate;
- NSMutableAttributedString* candHilitedTemplate =
+ NSMutableAttributedString* candidateTemplate =
+ _candidateTemplate.mutableCopy;
+ [candidateTemplate addAttribute:NSParagraphStyleAttributeName
+ value:candidateParagraphStyle
+ range:NSMakeRange(0, candidateTemplate.length)];
+ _candidateTemplate = candidateTemplate;
+ NSMutableAttributedString* candidateHilitedTemplate =
_candidateHilitedTemplate.mutableCopy;
- [candHilitedTemplate
+ [candidateHilitedTemplate
addAttribute:NSParagraphStyleAttributeName
value:candidateParagraphStyle
- range:NSMakeRange(0, candHilitedTemplate.length)];
- _candidateHilitedTemplate = candHilitedTemplate;
+ range:NSMakeRange(0, candidateHilitedTemplate.length)];
+ _candidateHilitedTemplate = candidateHilitedTemplate;
if (_tabular) {
- NSMutableAttributedString* candDimmedTemplate =
+ NSMutableAttributedString* candidateDimmedTemplate =
_candidateDimmedTemplate.mutableCopy;
- [candDimmedTemplate
+ [candidateDimmedTemplate
addAttribute:NSParagraphStyleAttributeName
value:candidateParagraphStyle
- range:NSMakeRange(0, candDimmedTemplate.length)];
- _candidateDimmedTemplate = candDimmedTemplate;
+ range:NSMakeRange(0, candidateDimmedTemplate.length)];
+ _candidateDimmedTemplate = candidateDimmedTemplate;
}
}
}
@@ -2028,6 +2035,17 @@ - (void)setScriptVariant:(NSString*)scriptVariant {
__attribute__((objc_direct_members))
@interface SquirrelLayoutManager : NSLayoutManager
+
+typedef NS_CLOSED_ENUM(NSUInteger, SquirrelContentBlock) {
+ kPreeditBlock,
+ kLinearCandidatesBlock,
+ kStackedCandidatesBlock,
+ kPagingBlock,
+ kStatusBlock
+};
+
+@property(nonatomic) SquirrelContentBlock contentBlock;
+
@end
@implementation SquirrelLayoutManager
@@ -2039,7 +2057,8 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(NSPoint)origin {
[self textContainerForGlyphAtIndex:glyphsToShow.location
effectiveRange:NULL
withoutAdditionalLayout:YES];
- BOOL verticalOrientation = (BOOL)textContainer.layoutOrientation;
+ BOOL verticalOrientation =
+ textContainer.layoutOrientation == NSTextLayoutOrientationVertical;
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
CGContextResetClip(context);
[self.textStorage
@@ -2057,7 +2076,7 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(NSPoint)origin {
effectiveRange:NULL
withoutAdditionalLayout:YES];
CGContextSaveGState(context);
- if (attrs[(id)kCTRubyAnnotationAttributeName]) {
+ if (attrs[(id)kCTRubyAnnotationAttributeName] != nil) {
CGContextScaleCTM(context, 1.0, -1.0);
NSUInteger glyphIndex = glyphRange.location;
CTLineRef line = CTLineCreateWithAttributedString(
@@ -2113,10 +2132,9 @@ - (void)drawGlyphsForGlyphRange:(NSRange)glyphsToShow atPoint:(NSPoint)origin {
NSFont* refFont =
attrs[(id)kCTBaselineReferenceInfoAttributeName]
[(id)kCTBaselineReferenceFont];
- offset.y += runFont.ascender * 0.5 +
- runFont.descender * 0.5 -
- refFont.ascender * 0.5 -
- refFont.descender * 0.5;
+ offset.y += (runFont.ascender + runFont.descender -
+ refFont.ascender - refFont.descender) *
+ 0.5;
} else if (verticalOrientation &&
runFont.pointSize < 24 &&
[runFont.fontName
@@ -2159,7 +2177,8 @@ - (BOOL)layoutManager:(NSLayoutManager*)layoutManager
inTextContainer:(NSTextContainer*)textContainer
forGlyphRange:(NSRange)glyphRange {
BOOL didModify = NO;
- BOOL verticalOrientation = (BOOL)textContainer.layoutOrientation;
+ BOOL verticalOrientation =
+ textContainer.layoutOrientation == NSTextLayoutOrientationVertical;
NSRange charRange = [layoutManager characterRangeForGlyphRange:glyphRange
actualGlyphRange:NULL];
NSParagraphStyle* rulerAttrs =
@@ -2174,7 +2193,7 @@ - (BOOL)layoutManager:(NSLayoutManager*)layoutManager
attribute:(id)kCTBaselineReferenceInfoAttributeName
atIndex:charRange.location
effectiveRange:NULL][(id)kCTBaselineReferenceFont];
- baseline += refFont.ascender * 0.5 + refFont.descender * 0.5;
+ baseline += (refFont.ascender + refFont.descender) * 0.5;
}
CGFloat lineHeightDelta =
lineFragmentUsedRect->size.height - lineHeight - lineSpacing;
@@ -2185,10 +2204,6 @@ - (BOOL)layoutManager:(NSLayoutManager*)layoutManager
round(lineFragmentRect->size.height - lineHeightDelta);
didModify |= YES;
}
- // move half of the linespacing above the line fragment
- if (lineSpacing > 0.1) {
- baseline += lineSpacing * 0.5;
- }
CGFloat newBaselineOffset = floor(lineFragmentUsedRect->origin.y -
lineFragmentRect->origin.y + baseline);
if (fabs(*baselineOffset - newBaselineOffset) > 0.1) {
@@ -2205,15 +2220,8 @@ - (BOOL)layoutManager:(NSLayoutManager*)layoutManager
} else {
unichar charBeforeIndex = [layoutManager.textStorage.mutableString
characterAtIndex:charIndex - 1];
- NSTextAlignment alignment =
- [[layoutManager.textStorage attribute:NSParagraphStyleAttributeName
- atIndex:charIndex
- effectiveRange:NULL] alignment];
- if (alignment == NSTextAlignmentNatural) { // candidates in linear layout
- return charBeforeIndex == 0x1D;
- } else {
- return charBeforeIndex != '\t';
- }
+ return _contentBlock == kLinearCandidatesBlock ? charBeforeIndex == 0x1D
+ : charBeforeIndex != '\t';
}
}
@@ -2246,7 +2254,7 @@ - (NSRect)layoutManager:(NSLayoutManager*)layoutManager
[layoutManager.textStorage attribute:(id)kCTRubyAnnotationAttributeName
atIndex:charIndex - 1
effectiveRange:&rubyRange];
- if (rubyAnnotation) {
+ if (rubyAnnotation != nil) {
NSAttributedString* rubyString =
[layoutManager.textStorage attributedSubstringFromRange:rubyRange];
CTLineRef line =
@@ -2256,7 +2264,8 @@ - (NSRect)layoutManager:(NSLayoutManager*)layoutManager
width = fdim(rubyRect.size.width, rubyString.size.width);
}
}
- return NSMakeRect(glyphPosition.x, 0.0, width, glyphPosition.y);
+ return NSMakeRect(glyphPosition.x, glyphPosition.y, width,
+ NSMaxY(proposedRect) - glyphPosition.y);
}
@end // SquirrelLayoutManager
@@ -2265,15 +2274,10 @@ - (NSRect)layoutManager:(NSLayoutManager*)layoutManager
API_AVAILABLE(macos(12.0))
@interface SquirrelTextLayoutFragment : NSTextLayoutFragment
-
-@property(nonatomic) CGFloat topMargin;
-
@end
@implementation SquirrelTextLayoutFragment
-@synthesize topMargin;
-
- (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context {
if (@available(macOS 14.0, *)) {
} else { // in macOS 12 and 13, textLineFragments.typographicBouonds are in
@@ -2282,25 +2286,22 @@ - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context {
point.y -= self.layoutFragmentFrame.origin.y;
}
BOOL verticalOrientation =
- (BOOL)self.textLayoutManager.textContainer.layoutOrientation;
+ self.textLayoutManager.textContainer.layoutOrientation ==
+ NSTextLayoutOrientationVertical;
for (NSTextLineFragment* lineFrag in self.textLineFragments) {
CGRect lineRect =
CGRectOffset(lineFrag.typographicBounds, point.x, point.y);
- CGFloat lineSpacing =
- [[lineFrag.attributedString attribute:NSParagraphStyleAttributeName
- atIndex:lineFrag.characterRange.location
- effectiveRange:NULL] lineSpacing];
- CGFloat baseline = CGRectGetMidY(lineRect) - lineSpacing * 0.5;
+ CGFloat baseline = CGRectGetMidY(lineRect);
if (!verticalOrientation) {
NSFont* refFont = [lineFrag.attributedString
attribute:(id)kCTBaselineReferenceInfoAttributeName
atIndex:lineFrag.characterRange.location
effectiveRange:NULL][(id)kCTBaselineReferenceFont];
- baseline += refFont.ascender * 0.5 + refFont.descender * 0.5;
+ baseline += (refFont.ascender + refFont.descender) * 0.5;
}
CGPoint renderOrigin =
CGPointMake(NSMinX(lineRect) + lineFrag.glyphOrigin.x,
- ceil(baseline) - lineFrag.glyphOrigin.y);
+ floor(baseline) - lineFrag.glyphOrigin.y);
CGPoint deviceOrigin =
CGContextConvertPointToDeviceSpace(context, renderOrigin);
renderOrigin = CGContextConvertPointToUserSpace(
@@ -2314,6 +2315,9 @@ - (void)drawAtPoint:(CGPoint)point inContext:(CGContextRef)context {
__attribute__((objc_direct_members)) API_AVAILABLE(macos(12.0))
@interface SquirrelTextLayoutManager
: NSTextLayoutManager
+
+@property(nonatomic) SquirrelContentBlock contentBlock;
+
@end
@implementation SquirrelTextLayoutManager
@@ -2322,7 +2326,7 @@ - (BOOL)textLayoutManager:(NSTextLayoutManager*)textLayoutManager
shouldBreakLineBeforeLocation:(id)location
hyphenating:(BOOL)hyphenating {
NSTextContentStorage* contentStorage =
- textLayoutManager.textContainer.textView.textContentStorage;
+ (NSTextContentStorage*)textLayoutManager.textContentManager;
NSUInteger charIndex = (NSUInteger)
[contentStorage offsetFromLocation:contentStorage.documentRange.location
toLocation:location];
@@ -2331,15 +2335,8 @@ - (BOOL)textLayoutManager:(NSTextLayoutManager*)textLayoutManager
} else {
unichar charBeforeIndex = [contentStorage.textStorage.mutableString
characterAtIndex:charIndex - 1];
- NSTextAlignment alignment =
- [[contentStorage.textStorage attribute:NSParagraphStyleAttributeName
- atIndex:charIndex
- effectiveRange:NULL] alignment];
- if (alignment == NSTextAlignmentNatural) { // candidates in linear layout
- return charBeforeIndex == 0x1D;
- } else {
- return charBeforeIndex != '\t';
- }
+ return _contentBlock == kLinearCandidatesBlock ? charBeforeIndex == 0x1D
+ : charBeforeIndex != '\t';
}
}
@@ -2353,14 +2350,6 @@ - (NSTextLayoutFragment*)textLayoutManager:
SquirrelTextLayoutFragment* fragment =
[SquirrelTextLayoutFragment.alloc initWithTextElement:textElement
range:textRange];
- NSTextStorage* textStorage =
- textLayoutManager.textContainer.textView.textContentStorage.textStorage;
- if (textStorage.length > 0 &&
- [location isEqual:self.documentRange.location]) {
- fragment.topMargin = [[textStorage attribute:NSParagraphStyleAttributeName
- atIndex:0
- effectiveRange:NULL] lineSpacing];
- }
return fragment;
}
@@ -2368,13 +2357,25 @@ - (NSTextLayoutFragment*)textLayoutManager:
#pragma mark - View behind text, containing drawings of backgrounds and highlights
+__attribute__((objc_direct_members))
+@interface NSFlippedView : NSView
+@end
+
+@implementation NSFlippedView
+
+- (BOOL)isFlipped {
+ return YES;
+}
+
+@end
+
__attribute__((objc_direct_members))
@interface SquirrelView : NSView
typedef struct {
- NSRect leading;
+ NSRect head;
NSRect body;
- NSRect trailing;
+ NSRect tail;
} SquirrelTextPolygon;
typedef struct {
@@ -2398,7 +2399,15 @@ @interface SquirrelView : NSView
API_AVAILABLE(macosx(10.14)) SquirrelTheme* darkTheme;
@property(nonatomic, readonly, strong, nonnull) SquirrelTheme* currentTheme;
@property(nonatomic, readonly, strong, nonnull) NSTextView* textView;
-@property(nonatomic, readonly, strong, nonnull) NSTextStorage* textStorage;
+@property(nonatomic, readonly, strong, nonnull) NSTextView* preeditView;
+@property(nonatomic, readonly, strong, nonnull) NSTextView* pagingView;
+@property(nonatomic, readonly, strong, nonnull) NSTextView* statusView;
+@property(nonatomic, readonly, strong, nonnull) NSScrollView* scrollView;
+@property(nonatomic, readonly, strong, nonnull) NSFlippedView* documentView;
+@property(nonatomic, readonly, strong, nonnull) NSTextStorage* contents;
+@property(nonatomic, readonly, strong, nonnull) NSTextStorage* preeditContents;
+@property(nonatomic, readonly, strong, nonnull) NSTextStorage* pagingContents;
+@property(nonatomic, readonly, strong, nonnull) NSTextStorage* statusContents;
@property(nonatomic, readonly, strong, nonnull) CAShapeLayer* shape;
@property(nonatomic, readonly, nullable) SquirrelTabularIndex* tabularIndices;
@property(nonatomic, readonly, nullable) SquirrelTextPolygon* candidatePolygons;
@@ -2406,63 +2415,69 @@ @interface SquirrelView : NSView
@property(nonatomic, readonly, nullable)
SquirrelCandidateRanges* candidateRanges;
@property(nonatomic, readonly, nullable) BOOL* truncated;
+@property(nonatomic, readonly) NSRect screen;
@property(nonatomic, readonly) NSRect contentRect;
-@property(nonatomic, readonly) NSRect preeditBlock;
-@property(nonatomic, readonly) NSRect candidateBlock;
-@property(nonatomic, readonly) NSRect pagingBlock;
+@property(nonatomic, readonly) NSRect documentRect;
+@property(nonatomic, readonly) NSRect preeditRect;
+@property(nonatomic, readonly) NSRect candidatesRect;
+@property(nonatomic, readonly) NSRect pagingRect;
@property(nonatomic, readonly) NSRect deleteBackRect;
@property(nonatomic, readonly) NSRect expanderRect;
@property(nonatomic, readonly) NSRect pageUpRect;
@property(nonatomic, readonly) NSRect pageDownRect;
-@property(nonatomic, readonly) SquirrelAppear appear;
+@property(nonatomic, readonly) CGFloat clippedHeight;
+@property(nonatomic, readonly) SquirrelAppearance appear;
+@property(nonatomic, readonly) SquirrelIndex cursorIndex;
@property(nonatomic, readonly) SquirrelIndex functionButton;
-@property(nonatomic, readonly) NSEdgeInsets marginInsets;
@property(nonatomic, readonly) NSUInteger candidateCount;
@property(nonatomic, readonly) NSUInteger hilitedIndex;
-@property(nonatomic, readonly) NSRange preeditRange;
@property(nonatomic, readonly) NSRange hilitedPreeditRange;
-@property(nonatomic, readonly) NSRange pagingRange;
-@property(nonatomic, readonly) CGFloat trailPadding;
@property(nonatomic) BOOL expanded;
+- (void)estimateBoundsOnScreen:(NSRect)screen
+ withPreedit:(BOOL)hasPreedit
+ candidates:(SquirrelCandidateRanges*)candidateRanges
+ truncation:(BOOL*)truncated
+ count:(NSUInteger)candidateCount
+ paging:(BOOL)hasPaging;
- (void)layoutContents;
-
-- (NSRect)blockRectForRange:(NSRange)range;
-
-- (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange;
-
-- (void)estimateBoundsForPreedit:(NSRange)preeditRange
- candidates:(SquirrelCandidateRanges*)candidateRanges
- truncation:(BOOL*)truncated
- count:(NSUInteger)candidateCount
- paging:(NSRange)pagingRange;
-
-- (void)drawViewWithInsets:(NSEdgeInsets)marginInsets
- hilitedIndex:(NSUInteger)hilitedIndex
- hilitedPreeditRange:(NSRange)hilitedPreeditRange;
-
-- (void)setPreeditRange:(NSRange)preeditRange
- hilitedPreeditRange:(NSRange)hilitedPreeditRange;
-
+- (NSRect)blockRectForRange:(NSRange)charRange inView:(NSTextView*)view;
+- (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange
+ inView:(NSTextView*)view;
+- (void)drawViewWithHilitedIndex:(NSUInteger)hilitedIndex
+ hilitedPreeditRange:(NSRange)hilitedPreeditRange;
+- (void)setHilitedPreeditRange:(NSRange)hilitedPreeditRange;
- (void)highlightCandidate:(NSUInteger)hilitedIndex;
-
- (void)highlightFunctionButton:(SquirrelIndex)functionButton;
-
- (SquirrelIndex)getIndexFromMouseSpot:(NSPoint)spot;
@end
@implementation SquirrelView
+static inline NSUInteger NSMaxRange(SquirrelCandidateRanges ranges) {
+ return ranges.location + ranges.length;
+}
+static inline NSRange RangeCandidate(SquirrelCandidateRanges ranges) {
+ return NSMakeRange(ranges.location, ranges.length);
+}
+static inline NSRange RangeLabel(SquirrelCandidateRanges ranges) {
+ return NSMakeRange(ranges.location, ranges.text);
+}
+static inline NSRange RangeText(SquirrelCandidateRanges ranges) {
+ return NSMakeRange(ranges.location + ranges.text,
+ ranges.comment - ranges.text);
+}
+static inline NSRange RangeComment(SquirrelCandidateRanges ranges) {
+ return NSMakeRange(ranges.location + ranges.comment,
+ ranges.length - ranges.comment);
+}
+
static SquirrelTheme* _defaultTheme = SquirrelTheme.alloc.init;
static SquirrelTheme* _darkTheme API_AVAILABLE(macos(10.14)) =
SquirrelTheme.alloc.init;
-NS_INLINE NSUInteger NSMaxRange(SquirrelCandidateRanges ranges) {
- return (ranges.location + ranges.length);
-}
-
-// Need flipped coordinate system, as required by textStorage
+// Need flipped coordinate system, consistent with textView and textContainer
- (BOOL)isFlipped {
return YES;
}
@@ -2471,12 +2486,15 @@ - (BOOL)wantsUpdateLayer {
return YES;
}
-- (void)setAppear:(SquirrelAppear)appear {
+- (void)setAppear:(SquirrelAppearance)appear {
if (@available(macOS 10.14, *)) {
if (_appear != appear) {
_appear = appear;
- [self setValue:appear == darkAppear ? _darkTheme : _defaultTheme
+ [self setValue:appear == kDarkAppearance ? _darkTheme : _defaultTheme
forKey:@"currentTheme"];
+ [self setValue:appear == kDarkAppearance ? @(NSScrollerKnobStyleLight)
+ : @(NSScrollerKnobStyleDark)
+ forKeyPath:@"scrollView.scrollerKnobStyle"];
}
}
}
@@ -2489,114 +2507,251 @@ + (SquirrelTheme*)darkTheme API_AVAILABLE(macos(10.14)) {
return _darkTheme;
}
-- (instancetype)initWithFrame:(NSRect)frameRect {
- self = [super initWithFrame:frameRect];
- if (self) {
+static NSTextView* setupTextViewForContentBlock(
+ SquirrelContentBlock contentBlock,
+ NSTextStorage* textStorage) {
+ NSTextContainer* textContainer =
+ [NSTextContainer.alloc initWithSize:NSZeroSize];
+ textContainer.lineFragmentPadding = 0;
+ if (@available(macOS 12.0, *)) {
+ SquirrelTextLayoutManager* textLayoutManager =
+ SquirrelTextLayoutManager.alloc.init;
+ textLayoutManager.contentBlock = contentBlock;
+ textLayoutManager.usesFontLeading = NO;
+ textLayoutManager.usesHyphenation = NO;
+ textLayoutManager.delegate = textLayoutManager;
+ textLayoutManager.textContainer = textContainer;
+ NSTextContentStorage* contentStorage = NSTextContentStorage.alloc.init;
+ [contentStorage addTextLayoutManager:textLayoutManager];
+ contentStorage.textStorage = textStorage;
+ } else {
+ SquirrelLayoutManager* layoutManager = SquirrelLayoutManager.alloc.init;
+ layoutManager.contentBlock = contentBlock;
+ layoutManager.backgroundLayoutEnabled = YES;
+ layoutManager.usesFontLeading = NO;
+ layoutManager.typesetterBehavior = NSTypesetterLatestBehavior;
+ layoutManager.delegate = layoutManager;
+ [layoutManager addTextContainer:textContainer];
+ [textStorage addLayoutManager:layoutManager];
+ }
+ NSTextView* textView = [NSTextView.alloc initWithFrame:NSZeroRect
+ textContainer:textContainer];
+ textView.drawsBackground = NO;
+ textView.selectable = NO;
+ textView.wantsLayer = YES;
+ textView.layer.geometryFlipped = YES;
+ textView.layerContentsRedrawPolicy =
+ NSViewLayerContentsRedrawOnSetNeedsDisplay;
+ textView.clipsToBounds = NO;
+ return textView;
+}
+
+- (instancetype)init {
+ if (self = [super init]) {
self.wantsLayer = YES;
self.layer.geometryFlipped = YES;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
- if (@available(macOS 12.0, *)) {
- SquirrelTextLayoutManager* textLayoutManager =
- SquirrelTextLayoutManager.alloc.init;
- textLayoutManager.usesFontLeading = NO;
- textLayoutManager.usesHyphenation = NO;
- textLayoutManager.delegate = textLayoutManager;
- NSTextContainer* textContainer =
- [NSTextContainer.alloc initWithSize:NSZeroSize];
- textContainer.lineFragmentPadding = 0;
- textLayoutManager.textContainer = textContainer;
- NSTextContentStorage* contentStorage = NSTextContentStorage.alloc.init;
- _textStorage = contentStorage.textStorage;
- [contentStorage addTextLayoutManager:textLayoutManager];
- _textView = [NSTextView.alloc initWithFrame:frameRect
- textContainer:textContainer];
- } else {
- SquirrelLayoutManager* layoutManager = SquirrelLayoutManager.alloc.init;
- layoutManager.backgroundLayoutEnabled = YES;
- layoutManager.usesFontLeading = NO;
- layoutManager.typesetterBehavior = NSTypesetterLatestBehavior;
- layoutManager.delegate = layoutManager;
- NSTextContainer* textContainer =
- [NSTextContainer.alloc initWithContainerSize:NSZeroSize];
- textContainer.lineFragmentPadding = 0;
- [layoutManager addTextContainer:textContainer];
- _textStorage = NSTextStorage.alloc.init;
- [_textStorage addLayoutManager:layoutManager];
- _textView = [NSTextView.alloc initWithFrame:frameRect
- textContainer:textContainer];
- }
- _textView.drawsBackground = NO;
- _textView.selectable = NO;
- _textView.wantsLayer = YES;
-
- _appear = defaultAppear;
+ _contents = NSTextStorage.alloc.init;
+ _textView =
+ setupTextViewForContentBlock(kStackedCandidatesBlock, _contents);
+ _preeditContents = NSTextStorage.alloc.init;
+ _preeditView =
+ setupTextViewForContentBlock(kPreeditBlock, _preeditContents);
+ _pagingContents = NSTextStorage.alloc.init;
+ _pagingView = setupTextViewForContentBlock(kPagingBlock, _pagingContents);
+ _statusContents = NSTextStorage.alloc.init;
+ _statusView = setupTextViewForContentBlock(kStatusBlock, _statusContents);
+
+ _documentView = NSFlippedView.alloc.init;
+ _documentView.wantsLayer = YES;
+ [_documentView addSubview:_textView];
+ _scrollView = NSScrollView.alloc.init;
+ _scrollView.documentView = _documentView;
+ _scrollView.drawsBackground = NO;
+ _scrollView.automaticallyAdjustsContentInsets = NO;
+ _scrollView.hasVerticalScroller = YES;
+ _scrollView.scrollerStyle = NSScrollerStyleOverlay;
+ _scrollView.scrollerKnobStyle = NSScrollerKnobStyleDark;
+
+ _appear = kDefaultAppearance;
_currentTheme = _defaultTheme;
_shape = CAShapeLayer.alloc.init;
}
return self;
}
-- (NSTextRange*)getTextRangeFromCharRange:(NSRange)charRange
+- (NSTextRange*)textRangeFromCharRange:(NSRange)charRange
+ inView:(NSTextView*)view
API_AVAILABLE(macos(12.0)) {
if (charRange.location == NSNotFound) {
return nil;
} else {
- NSTextContentStorage* contentStorage = _textView.textContentStorage;
- id startLocation = [contentStorage
- locationFromLocation:contentStorage.documentRange.location
- withOffset:(NSInteger)charRange.location];
+ NSTextContentStorage* storage = view.textContentStorage;
+ id startLocation =
+ [storage locationFromLocation:storage.documentRange.location
+ withOffset:(NSInteger)charRange.location];
id endLocation =
- [contentStorage locationFromLocation:startLocation
- withOffset:(NSInteger)charRange.length];
+ [storage locationFromLocation:startLocation
+ withOffset:(NSInteger)charRange.length];
return [NSTextRange.alloc initWithLocation:startLocation
endLocation:endLocation];
}
}
-- (NSRange)getCharRangeFromTextRange:(NSTextRange*)textRange
- API_AVAILABLE(macos(12.0)) {
+- (NSRange)charRangeFromTextRange:(NSTextRange*)textRange
+ inView:(NSTextView*)view API_AVAILABLE(macos(12.0)) {
if (textRange == nil) {
return NSMakeRange(NSNotFound, 0);
} else {
- NSTextContentStorage* contentStorage = _textView.textContentStorage;
+ NSTextContentStorage* storage = view.textContentStorage;
NSInteger location =
- [contentStorage offsetFromLocation:contentStorage.documentRange.location
- toLocation:textRange.location];
- NSInteger length =
- [contentStorage offsetFromLocation:textRange.location
- toLocation:textRange.endLocation];
+ [storage offsetFromLocation:storage.documentRange.location
+ toLocation:textRange.location];
+ NSInteger length = [storage offsetFromLocation:textRange.location
+ toLocation:textRange.endLocation];
return NSMakeRange((NSUInteger)location, (NSUInteger)length);
}
}
-// Get the rectangle containing entire contents
-- (void)layoutContents {
+static NSRect layoutTextView(NSTextView* view) {
if (@available(macOS 12.0, *)) {
- [_textView.textLayoutManager
- ensureLayoutForRange:_textView.textContentStorage.documentRange];
- _contentRect = _textView.textLayoutManager.usageBoundsForTextContainer;
+ [view.textLayoutManager
+ ensureLayoutForRange:view.textContentStorage.documentRange];
+ return view.textLayoutManager.usageBoundsForTextContainer;
} else {
- [_textView.layoutManager
- ensureLayoutForTextContainer:_textView.textContainer];
- _contentRect = [_textView.layoutManager
- usedRectForTextContainer:_textView.textContainer];
+ [view.layoutManager ensureLayoutForTextContainer:view.textContainer];
+ return [view.layoutManager usedRectForTextContainer:view.textContainer];
+ }
+}
+
+static BOOL any(BOOL* array, NSUInteger count) {
+ for (NSUInteger i = 0; i < count; ++i) {
+ if (array[i]) {
+ return YES;
+ }
}
- _contentRect.size =
- NSMakeSize(ceil(NSWidth(_contentRect)), ceil(NSHeight(_contentRect)));
+ return NO;
+}
+
+- (void)estimateBoundsOnScreen:(NSRect)screen
+ withPreedit:(BOOL)hasPreedit
+ candidates:(SquirrelCandidateRanges*)candidateRanges
+ truncation:(BOOL*)truncated
+ count:(NSUInteger)candidateCount
+ paging:(BOOL)hasPaging {
+ _screen = screen;
+ _candidateRanges = candidateRanges;
+ _truncated = truncated;
+ _candidateCount = candidateCount;
+ _preeditView.hidden = !hasPreedit;
+ _scrollView.hidden = candidateCount == 0;
+ _pagingView.hidden = !hasPaging;
+ _statusView.hidden = hasPreedit || candidateCount > 0;
+ // layout textviews and get their sizes
+ _preeditRect = NSZeroRect;
+ _documentRect = NSZeroRect; // in textView's own coordinates
+ _candidatesRect = NSZeroRect;
+ _pagingRect = NSZeroRect;
+ _clippedHeight = 0.0;
+ if (!hasPreedit && candidateCount == 0) { // status
+ _contentRect = layoutTextView(_statusView);
+ return;
+ }
+ if (hasPreedit) {
+ _preeditRect = layoutTextView(_preeditView);
+ _contentRect = _preeditRect;
+ }
+ if (candidateCount > 0) {
+ _documentRect = layoutTextView(_textView);
+ if (@available(macOS 12.0, *)) {
+ _documentRect.size.height += _currentTheme.linespace;
+ } else {
+ _documentRect.size.height +=
+ _currentTheme.linear ? 0.0 : _currentTheme.linespace;
+ }
+ if (_currentTheme.linear && !any(truncated, candidateCount)) {
+ _documentRect.size.width -= _currentTheme.fullWidth;
+ }
+ _candidatesRect.size = _documentRect.size;
+ _documentRect.size.width += _currentTheme.fullWidth;
+ if (hasPreedit) {
+ _candidatesRect.origin.y =
+ NSMaxY(_preeditRect) + _currentTheme.preeditLinespace;
+ _contentRect = NSUnionRect(_preeditRect, _candidatesRect);
+ } else {
+ _contentRect = _candidatesRect;
+ }
+ if (hasPaging) {
+ _pagingRect = layoutTextView(_pagingView);
+ _pagingRect.origin.y = NSMaxY(_candidatesRect);
+ _contentRect = NSUnionRect(_contentRect, _pagingRect);
+ }
+ } else {
+ return;
+ }
+ // clip candidate block if it has too many lines
+ CGFloat maxHeight =
+ (_currentTheme.vertical ? NSWidth(_screen) : NSHeight(_screen)) * 0.5 -
+ _currentTheme.borderInsets.height * 2;
+ _clippedHeight = fdim(ceil(NSHeight(_contentRect)), ceil(maxHeight));
+ _contentRect.size.height -= _clippedHeight;
+ _candidatesRect.size.height -= _clippedHeight;
+ _scrollView.verticalScroller.knobProportion =
+ NSHeight(_candidatesRect) / NSHeight(_documentRect);
+}
+
+// Get the rectangle containing entire contents
+- (void)layoutContents {
+ NSPoint origin = NSMakePoint(_currentTheme.borderInsets.width,
+ _currentTheme.borderInsets.height);
+ if (!_statusView.hidden) { // status
+ _contentRect.origin =
+ NSMakePoint(origin.x + ceil(_currentTheme.fullWidth * 0.5), origin.y);
+ return;
+ }
+ if (!_preeditView.hidden) {
+ _preeditRect = layoutTextView(_preeditView);
+ _preeditRect.size.width += _currentTheme.fullWidth;
+ _preeditRect.origin = origin;
+ _contentRect = _preeditRect;
+ }
+ if (!_scrollView.hidden) {
+ _candidatesRect.size.width = NSWidth(_documentRect);
+ _candidatesRect.size.height = NSHeight(_documentRect) - _clippedHeight;
+ if (!_preeditView.hidden) {
+ _candidatesRect.origin.x = origin.x;
+ _candidatesRect.origin.y =
+ NSMaxY(_preeditRect) + _currentTheme.preeditLinespace;
+ _contentRect = NSUnionRect(_preeditRect, _candidatesRect);
+ } else {
+ _candidatesRect.origin = origin;
+ _contentRect = _candidatesRect;
+ }
+ if (!_pagingView.hidden) {
+ _pagingRect = layoutTextView(_pagingView);
+ _pagingRect.size.width += _currentTheme.fullWidth;
+ _pagingRect.origin.x = origin.x;
+ _pagingRect.origin.y = NSMaxY(_candidatesRect);
+ _contentRect = NSUnionRect(_contentRect, _pagingRect);
+ }
+ }
+ _contentRect.size.width -= _currentTheme.fullWidth;
+ _contentRect.origin.x += ceil(_currentTheme.fullWidth * 0.5);
}
// Get the rectangle containing the range of text, will first convert to glyph
// or text range, expensive to calculate
-- (NSRect)blockRectForRange:(NSRange)charRange {
+- (NSRect)blockRectForRange:(NSRange)charRange inView:(NSTextView*)view {
if (charRange.location == NSNotFound) {
return NSZeroRect;
}
if (@available(macOS 12.0, *)) {
- NSTextRange* textRange = [self getTextRangeFromCharRange:charRange];
- NSRect __block firstLineRect = CGRectNull;
- NSRect __block finalLineRect = CGRectNull;
- [_textView.textLayoutManager
+ NSTextRange* textRange = [self textRangeFromCharRange:charRange
+ inView:view];
+ NSRect __block firstLineRect = NSZeroRect;
+ NSRect __block finalLineRect = NSZeroRect;
+ [view.textLayoutManager
enumerateTextSegmentsInRange:textRange
type:NSTextLayoutManagerSegmentTypeStandard
options:
@@ -2618,73 +2773,72 @@ - (NSRect)blockRectForRange:(NSRange)charRange {
}
return YES;
}];
- if (_currentTheme.linear && _currentTheme.linespace > 0.1 &&
- _candidateCount > 0) {
- if (charRange.location >= _candidateRanges[0].location &&
- charRange.location <
- NSMaxRange(_candidateRanges[_candidateCount - 1])) {
- firstLineRect.size.height += _currentTheme.linespace;
- firstLineRect.origin.y -= _currentTheme.linespace;
- }
- if (!NSIsEmptyRect(finalLineRect) &&
- NSMaxRange(charRange) > _candidateRanges[0].location &&
- NSMaxRange(charRange) <=
- NSMaxRange(_candidateRanges[_candidateCount - 1])) {
- finalLineRect.size.height += _currentTheme.linespace;
- finalLineRect.origin.y -= _currentTheme.linespace;
+ CGFloat lineSpacing = view.hash == _textView.hash && _currentTheme.linear
+ ? _currentTheme.linespace
+ : 0.0;
+ if (lineSpacing > 0.1) {
+ firstLineRect.size.height += lineSpacing;
+ if (!NSIsEmptyRect(finalLineRect)) {
+ finalLineRect.size.height += lineSpacing;
}
}
if (NSIsEmptyRect(finalLineRect)) {
return firstLineRect;
} else {
- return NSMakeRect(0.0, NSMinY(firstLineRect),
- NSMaxX(_contentRect) - _trailPadding,
+ CGFloat containerWidth =
+ NSWidth([view.textLayoutManager usageBoundsForTextContainer]);
+ return NSMakeRect(0.0, NSMinY(firstLineRect), containerWidth,
NSMaxY(finalLineRect) - NSMinY(firstLineRect));
}
} else {
- NSLayoutManager* layoutManager = _textView.layoutManager;
- NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange
- actualCharacterRange:NULL];
+ NSRange glyphRange =
+ [view.layoutManager glyphRangeForCharacterRange:charRange
+ actualCharacterRange:NULL];
NSRange firstLineRange = NSMakeRange(NSNotFound, 0);
- NSRect firstLineRect =
- [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location
- effectiveRange:&firstLineRange];
+ NSRect firstLineRect = [view.layoutManager
+ lineFragmentUsedRectForGlyphAtIndex:glyphRange.location
+ effectiveRange:&firstLineRange];
if (NSMaxRange(glyphRange) <= NSMaxRange(firstLineRange)) {
- CGFloat headX =
- [layoutManager locationForGlyphAtIndex:glyphRange.location].x;
- CGFloat tailX =
+ CGFloat leading =
+ [view.layoutManager locationForGlyphAtIndex:glyphRange.location].x;
+ CGFloat trailing =
NSMaxRange(glyphRange) < NSMaxRange(firstLineRange)
- ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x
- : NSWidth(firstLineRect);
- return NSMakeRect(NSMinX(firstLineRect) + headX, NSMinY(firstLineRect),
- tailX - headX, NSHeight(firstLineRect));
+ ? [view.layoutManager
+ locationForGlyphAtIndex:NSMaxRange(glyphRange)]
+ .x
+ : NSMaxX(firstLineRect);
+ return NSMakeRect(NSMinX(firstLineRect) + leading, NSMinY(firstLineRect),
+ trailing - leading, NSHeight(firstLineRect));
} else {
- NSRect finalLineRect = [layoutManager
+ NSRect finalLineRect = [view.layoutManager
lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1
effectiveRange:NULL];
- return NSMakeRect(0.0, NSMinY(firstLineRect),
- NSMaxX(_contentRect) - _trailPadding,
+ CGFloat containerWidth = NSWidth(
+ [view.layoutManager usedRectForTextContainer:view.textContainer]);
+ return NSMakeRect(0.0, NSMinY(firstLineRect), containerWidth,
NSMaxY(finalLineRect) - NSMinY(firstLineRect));
}
}
}
-// Calculate 3 boxes containing the text in range. leadingRect and trailingRect
-// are incomplete line rectangle bodyRect is the complete line fragment in the
-// middle if the range spans no less than one full line
-- (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange {
+/* Calculate 3 rectangles encloding the text in range. TextPolygon.head & .tail
+ are incomplete line fragments TextPolygon.body is the complete line fragment
+ in the middle if the range spans no less than one full line */
+- (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange
+ inView:(NSTextView*)view {
SquirrelTextPolygon textPolygon = {
- .leading = NSZeroRect, .body = NSZeroRect, .trailing = NSZeroRect};
+ .head = NSZeroRect, .body = NSZeroRect, .tail = NSZeroRect};
if (charRange.location == NSNotFound) {
return textPolygon;
}
if (@available(macOS 12.0, *)) {
- NSTextRange* textRange = [self getTextRangeFromCharRange:charRange];
- NSRect __block leadingLineRect = CGRectNull;
- NSRect __block trailingLineRect = CGRectNull;
- NSTextRange __block* leadingLineRange;
- NSTextRange __block* trailingLineRange;
- [_textView.textLayoutManager
+ NSTextRange* textRange = [self textRangeFromCharRange:charRange
+ inView:view];
+ NSRect __block headLineRect = NSZeroRect;
+ NSRect __block tailLineRect = NSZeroRect;
+ NSTextRange __block* headLineRange;
+ NSTextRange __block* tailLineRange;
+ [view.textLayoutManager
enumerateTextSegmentsInRange:textRange
type:NSTextLayoutManagerSegmentTypeStandard
options:
@@ -2694,130 +2848,128 @@ - (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange {
CGFloat baseline,
NSTextContainer* _Nonnull textContainer) {
if (!CGRectIsEmpty(segFrame)) {
- if (NSIsEmptyRect(leadingLineRect) ||
+ if (NSIsEmptyRect(headLineRect) ||
CGRectGetMinY(segFrame) <
- NSMaxY(leadingLineRect)) {
- leadingLineRect =
- NSUnionRect(segFrame, leadingLineRect);
- leadingLineRange = [leadingLineRange
+ NSMaxY(headLineRect)) {
+ headLineRect =
+ NSUnionRect(segFrame, headLineRect);
+ headLineRange = [headLineRange
textRangeByFormingUnionWithTextRange:
segRange];
} else {
- trailingLineRect =
- NSUnionRect(segFrame, trailingLineRect);
- trailingLineRange = [trailingLineRange
+ tailLineRect =
+ NSUnionRect(segFrame, tailLineRect);
+ tailLineRange = [tailLineRange
textRangeByFormingUnionWithTextRange:
segRange];
}
}
return YES;
}];
- if (_currentTheme.linear && _currentTheme.linespace > 0.1 &&
- _candidateCount > 0) {
- if (charRange.location >= _candidateRanges[0].location &&
- charRange.location <
- NSMaxRange(_candidateRanges[_candidateCount - 1])) {
- leadingLineRect.size.height += _currentTheme.linespace;
- leadingLineRect.origin.y -= _currentTheme.linespace;
+ NSTextContentStorage* storage =
+ (NSTextContentStorage*)view.textLayoutManager.textContentManager;
+ NSParagraphStyle* rulerAttrs =
+ [storage.textStorage attribute:NSParagraphStyleAttributeName
+ atIndex:charRange.location
+ effectiveRange:NULL];
+ if (rulerAttrs.lineSpacing > 0.1) {
+ headLineRect.size.height += rulerAttrs.lineSpacing;
+ if (!NSIsEmptyRect(tailLineRect)) {
+ tailLineRect.size.height += rulerAttrs.lineSpacing;
}
}
- if (NSIsEmptyRect(trailingLineRect)) {
- textPolygon.body = leadingLineRect;
+ if (NSIsEmptyRect(tailLineRect)) {
+ textPolygon.body = headLineRect;
} else {
- if (_currentTheme.linear && _currentTheme.linespace > 0.1 &&
- _candidateCount > 0) {
- if (NSMaxRange(charRange) > _candidateRanges[0].location &&
- NSMaxRange(charRange) <=
- NSMaxRange(_candidateRanges[_candidateCount - 1])) {
- trailingLineRect.size.height += _currentTheme.linespace;
- trailingLineRect.origin.y -= _currentTheme.linespace;
- }
- }
-
- CGFloat containerWidth = NSMaxX(_contentRect) - _trailPadding;
- leadingLineRect.size.width = containerWidth - NSMinX(leadingLineRect);
- if (fabs(NSMaxX(trailingLineRect) - NSMaxX(leadingLineRect)) < 1) {
- if (fabs(NSMinX(leadingLineRect) - NSMinX(trailingLineRect)) < 1) {
- textPolygon.body = NSUnionRect(leadingLineRect, trailingLineRect);
+ CGFloat containerWidth =
+ NSWidth([view.textLayoutManager usageBoundsForTextContainer]);
+ headLineRect.size.width = containerWidth - NSMinX(headLineRect);
+ if (fabs(NSMaxX(tailLineRect) - NSMaxX(headLineRect)) < 1) {
+ if (fabs(NSMinX(headLineRect) - NSMinX(tailLineRect)) < 1) {
+ textPolygon.body = NSUnionRect(headLineRect, tailLineRect);
} else {
- textPolygon.leading = leadingLineRect;
+ textPolygon.head = headLineRect;
textPolygon.body =
- NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth,
- NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect));
+ NSMakeRect(0.0, NSMaxY(headLineRect), containerWidth,
+ NSMaxY(tailLineRect) - NSMaxY(headLineRect));
}
} else {
- textPolygon.trailing = trailingLineRect;
- if (fabs(NSMinX(leadingLineRect) - NSMinX(trailingLineRect)) < 1) {
+ textPolygon.tail = tailLineRect;
+ if (fabs(NSMinX(headLineRect) - NSMinX(tailLineRect)) < 1) {
textPolygon.body =
- NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth,
- NSMinY(trailingLineRect) - NSMinY(leadingLineRect));
+ NSMakeRect(0.0, NSMinY(headLineRect), containerWidth,
+ NSMinY(tailLineRect) - NSMinY(headLineRect));
} else {
- textPolygon.leading = leadingLineRect;
- if (![trailingLineRange
- containsLocation:leadingLineRange.endLocation]) {
+ textPolygon.head = headLineRect;
+ if (![tailLineRange containsLocation:headLineRange.endLocation]) {
textPolygon.body =
- NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth,
- NSMinY(trailingLineRect) - NSMaxY(leadingLineRect));
+ NSMakeRect(0.0, NSMaxY(headLineRect), containerWidth,
+ NSMinY(tailLineRect) - NSMaxY(headLineRect));
}
}
}
}
} else {
- NSLayoutManager* layoutManager = _textView.layoutManager;
- NSRange glyphRange = [layoutManager glyphRangeForCharacterRange:charRange
- actualCharacterRange:NULL];
- NSRange leadingLineRange = NSMakeRange(NSNotFound, 0);
- NSRect leadingLineRect =
- [layoutManager lineFragmentUsedRectForGlyphAtIndex:glyphRange.location
- effectiveRange:&leadingLineRange];
- CGFloat headX =
- [layoutManager locationForGlyphAtIndex:glyphRange.location].x;
- if (NSMaxRange(leadingLineRange) >= NSMaxRange(glyphRange)) {
- CGFloat tailX =
- NSMaxRange(glyphRange) < NSMaxRange(leadingLineRange)
- ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x
- : NSWidth(leadingLineRect);
- textPolygon.body = NSMakeRect(headX, NSMinY(leadingLineRect),
- tailX - headX, NSHeight(leadingLineRect));
+ NSRange glyphRange =
+ [view.layoutManager glyphRangeForCharacterRange:charRange
+ actualCharacterRange:NULL];
+ NSRange headLineRange = NSMakeRange(NSNotFound, 0);
+ NSRect headLineRect = [view.layoutManager
+ lineFragmentUsedRectForGlyphAtIndex:glyphRange.location
+ effectiveRange:&headLineRange];
+ CGFloat leading =
+ [view.layoutManager locationForGlyphAtIndex:glyphRange.location].x;
+ if (NSMaxRange(headLineRange) >= NSMaxRange(glyphRange)) {
+ CGFloat trailing =
+ NSMaxRange(glyphRange) < NSMaxRange(headLineRange)
+ ? [view.layoutManager
+ locationForGlyphAtIndex:NSMaxRange(glyphRange)]
+ .x
+ : NSMaxX(headLineRect);
+ textPolygon.body = NSMakeRect(leading, NSMinY(headLineRect),
+ trailing - leading, NSHeight(headLineRect));
} else {
- CGFloat containerWidth = NSMaxX(_contentRect) - _trailPadding;
- NSRange trailingLineRange = NSMakeRange(NSNotFound, 0);
- NSRect trailingLineRect = [layoutManager
+ CGFloat containerWidth = NSWidth(
+ [view.layoutManager usedRectForTextContainer:view.textContainer]);
+ NSRange tailLineRange = NSMakeRange(NSNotFound, 0);
+ NSRect tailLineRect = [view.layoutManager
lineFragmentUsedRectForGlyphAtIndex:NSMaxRange(glyphRange) - 1
- effectiveRange:&trailingLineRange];
- CGFloat tailX =
- NSMaxRange(glyphRange) < NSMaxRange(trailingLineRange)
- ? [layoutManager locationForGlyphAtIndex:NSMaxRange(glyphRange)].x
- : NSWidth(trailingLineRect);
- if (NSMaxRange(trailingLineRange) == NSMaxRange(glyphRange)) {
- if (glyphRange.location == leadingLineRange.location) {
+ effectiveRange:&tailLineRange];
+ CGFloat trailing =
+ NSMaxRange(glyphRange) < NSMaxRange(tailLineRange)
+ ? [view.layoutManager
+ locationForGlyphAtIndex:NSMaxRange(glyphRange)]
+ .x
+ : NSMaxX(tailLineRect);
+ if (NSMaxRange(tailLineRange) == NSMaxRange(glyphRange)) {
+ if (glyphRange.location == headLineRange.location) {
textPolygon.body =
- NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth,
- NSMaxY(trailingLineRect) - NSMinY(leadingLineRect));
+ NSMakeRect(0.0, NSMinY(headLineRect), containerWidth,
+ NSMaxY(tailLineRect) - NSMinY(headLineRect));
} else {
- textPolygon.leading =
- NSMakeRect(headX, NSMinY(leadingLineRect), containerWidth - headX,
- NSHeight(leadingLineRect));
+ textPolygon.head =
+ NSMakeRect(leading, NSMinY(headLineRect),
+ containerWidth - leading, NSHeight(headLineRect));
textPolygon.body =
- NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth,
- NSMaxY(trailingLineRect) - NSMaxY(leadingLineRect));
+ NSMakeRect(0.0, NSMaxY(headLineRect), containerWidth,
+ NSMaxY(tailLineRect) - NSMaxY(headLineRect));
}
} else {
- textPolygon.trailing = NSMakeRect(0.0, NSMinY(trailingLineRect), tailX,
- NSHeight(trailingLineRect));
- if (glyphRange.location == leadingLineRange.location) {
+ textPolygon.tail = NSMakeRect(0.0, NSMinY(tailLineRect), trailing,
+ NSHeight(tailLineRect));
+ if (glyphRange.location == headLineRange.location) {
textPolygon.body =
- NSMakeRect(0.0, NSMinY(leadingLineRect), containerWidth,
- NSMinY(trailingLineRect) - NSMinY(leadingLineRect));
+ NSMakeRect(0.0, NSMinY(headLineRect), containerWidth,
+ NSMinY(tailLineRect) - NSMinY(headLineRect));
} else {
- textPolygon.leading =
- NSMakeRect(headX, NSMinY(leadingLineRect), containerWidth - headX,
- NSHeight(leadingLineRect));
- if (trailingLineRange.location > NSMaxRange(leadingLineRange)) {
+ textPolygon.head =
+ NSMakeRect(leading, NSMinY(headLineRect),
+ containerWidth - leading, NSHeight(headLineRect));
+ if (tailLineRange.location > NSMaxRange(headLineRange)) {
textPolygon.body =
- NSMakeRect(0.0, NSMaxY(leadingLineRect), containerWidth,
- NSMinY(trailingLineRect) - NSMaxY(leadingLineRect));
+ NSMakeRect(0.0, NSMaxY(headLineRect), containerWidth,
+ NSMinY(tailLineRect) - NSMaxY(headLineRect));
}
}
}
@@ -2826,82 +2978,36 @@ - (SquirrelTextPolygon)textPolygonForRange:(NSRange)charRange {
return textPolygon;
}
-- (void)estimateBoundsForPreedit:(NSRange)preeditRange
- candidates:(SquirrelCandidateRanges*)candidateRanges
- truncation:(BOOL*)truncated
- count:(NSUInteger)candidateCount
- paging:(NSRange)pagingRange {
- _preeditRange = preeditRange;
- _candidateRanges = candidateRanges;
- _truncated = truncated;
- _candidateCount = candidateCount;
- _pagingRange = pagingRange;
- [self layoutContents];
- if (_currentTheme.linear && (candidateCount > 0 || preeditRange.length > 0)) {
- CGFloat width = 0.0;
- if (preeditRange.length > 0) {
- width = ceil(NSMaxX([self blockRectForRange:preeditRange]));
- }
- if (candidateCount > 0) {
- BOOL isTruncated = truncated[0];
- NSUInteger start = candidateRanges[0].location;
- for (NSUInteger i = 1; i <= candidateCount; ++i) {
- if (i == candidateCount || truncated[i] != isTruncated) {
- NSRect candidateRect = [self
- blockRectForRange:NSMakeRange(start,
- NSMaxRange(candidateRanges[i - 1]) -
- start)];
- width =
- fmax(width, ceil(NSMaxX(candidateRect)) -
- (isTruncated ? 0.0 : _currentTheme.fullWidth));
- if (i < candidateCount) {
- isTruncated = truncated[i];
- start = candidateRanges[i].location;
- }
- }
- }
- }
- if (pagingRange.length > 0) {
- width = fmax(width, ceil(NSMaxX([self blockRectForRange:pagingRange])));
- }
- _trailPadding = fmax(NSMaxX(_contentRect) - width, 0.0);
- } else {
- _trailPadding = 0.0;
- }
-}
-
-// Will triger - (void)updateLayer
-- (void)drawViewWithInsets:(NSEdgeInsets)marginInsets
- hilitedIndex:(NSUInteger)hilitedIndex
- hilitedPreeditRange:(NSRange)hilitedPreeditRange {
- _marginInsets = marginInsets;
+// Will triger `- (void)updateLayer`
+- (void)drawViewWithHilitedIndex:(NSUInteger)hilitedIndex
+ hilitedPreeditRange:(NSRange)hilitedPreeditRange {
_hilitedIndex = hilitedIndex;
_hilitedPreeditRange = hilitedPreeditRange;
_functionButton = kVoidSymbol;
- // invalidate Rect beyond bound of textview to clear any out-of-bound drawing
- // from last round
self.needsDisplayInRect = self.bounds;
- _textView.needsDisplayInRect = [self convertRect:self.bounds
- toView:_textView];
- [self layoutContents];
-}
-
-- (void)setPreeditRange:(NSRange)preeditRange
- hilitedPreeditRange:(NSRange)hilitedPreeditRange {
- if (_preeditRange.length != preeditRange.length) {
- for (NSUInteger i = 0; i < _candidateCount; ++i) {
- _candidateRanges[i].location +=
- preeditRange.length - _preeditRange.length;
+ if (!_statusView.hidden) {
+ _statusView.needsDisplayInRect = _statusView.bounds;
+ } else {
+ if (!_preeditView.hidden) {
+ _preeditView.needsDisplayInRect = _preeditView.bounds;
}
- if (_pagingRange.location != NSNotFound) {
- _pagingRange.location += preeditRange.length - _preeditRange.length;
+ // invalidate Rect beyond bound of textview to clear any out-of-bound
+ // drawing from last round
+ if (!_scrollView.hidden) {
+ _textView.needsDisplayInRect =
+ [_documentView convertRect:_documentView.bounds toView:_textView];
+ }
+ if (!_pagingView.hidden) {
+ _pagingView.needsDisplayInRect = _pagingView.bounds;
}
}
- _preeditRange = preeditRange;
+ [self layoutContents];
+}
+
+- (void)setHilitedPreeditRange:(NSRange)hilitedPreeditRange {
_hilitedPreeditRange = hilitedPreeditRange;
- self.needsDisplayInRect = _preeditBlock;
- _textView.needsDisplayInRect = [self convertRect:_preeditBlock
- toView:_textView];
+ self.needsDisplayInRect = _preeditRect;
+ _preeditView.needsDisplayInRect = _preeditView.bounds;
[self layoutContents];
}
@@ -2910,22 +3016,59 @@ - (void)highlightCandidate:(NSUInteger)hilitedIndex {
NSUInteger priorActivePage = _hilitedIndex / _currentTheme.pageSize;
NSUInteger newActivePage = hilitedIndex / _currentTheme.pageSize;
if (newActivePage != priorActivePage) {
- self.needsDisplayInRect = _sectionRects[priorActivePage];
- [_textView
- setNeedsDisplayInRect:[self convertRect:_sectionRects[priorActivePage]
- toView:_textView]
- avoidAdditionalLayout:YES];
+ self.needsDisplayInRect =
+ [_documentView convertRect:_sectionRects[priorActivePage]
+ toView:self];
+ _textView.needsDisplayInRect =
+ [_documentView convertRect:_sectionRects[priorActivePage]
+ toView:_textView];
+ }
+ self.needsDisplayInRect =
+ [_documentView convertRect:_sectionRects[newActivePage] toView:self];
+ _textView.needsDisplayInRect =
+ [_documentView convertRect:_sectionRects[newActivePage]
+ toView:_textView];
+
+ if (NSMinY(_sectionRects[newActivePage]) <
+ NSMinY(_scrollView.documentVisibleRect) - 0.1) {
+ NSPoint origin = _scrollView.contentView.bounds.origin;
+ origin.y -= NSMinY(_scrollView.documentVisibleRect) -
+ NSMinY(_sectionRects[newActivePage]);
+ [_scrollView.contentView scrollToPoint:origin];
+ _scrollView.verticalScroller.doubleValue =
+ NSMinY(_scrollView.documentVisibleRect) / _clippedHeight;
+ } else if (NSMaxY(_sectionRects[newActivePage]) >
+ NSMaxY(_scrollView.documentVisibleRect) + 0.1) {
+ NSPoint origin = _scrollView.contentView.bounds.origin;
+ origin.y += NSMaxY(_sectionRects[newActivePage]) -
+ NSMaxY(_scrollView.documentVisibleRect);
+ [_scrollView.contentView scrollToPoint:origin];
+ _scrollView.verticalScroller.doubleValue =
+ NSMinY(_scrollView.documentVisibleRect) / _clippedHeight;
}
- self.needsDisplayInRect = _sectionRects[newActivePage];
- [_textView
- setNeedsDisplayInRect:[self convertRect:_sectionRects[newActivePage]
- toView:_textView]
- avoidAdditionalLayout:YES];
} else {
- self.needsDisplayInRect = _candidateBlock;
- [_textView setNeedsDisplayInRect:[self convertRect:_candidateBlock
- toView:_textView]
- avoidAdditionalLayout:YES];
+ self.needsDisplayInRect = _candidatesRect;
+ _textView.needsDisplayInRect =
+ [_documentView convertRect:_documentView.bounds toView:_textView];
+
+ SquirrelTextPolygon polygons = _candidatePolygons[hilitedIndex];
+ CGFloat top = NSIsEmptyRect(polygons.head) ? NSMinY(polygons.body)
+ : NSMinY(polygons.head);
+ CGFloat bottom = NSIsEmptyRect(polygons.tail) ? NSMaxY(polygons.body)
+ : NSMaxY(polygons.tail);
+ if (NSMinY(_scrollView.documentVisibleRect) > top + 0.1) {
+ NSPoint origin = _scrollView.contentView.bounds.origin;
+ origin.y -= NSMinY(_scrollView.documentVisibleRect) - top;
+ [_scrollView.contentView scrollToPoint:origin];
+ _scrollView.verticalScroller.doubleValue =
+ NSMinY(_scrollView.documentVisibleRect) / _clippedHeight;
+ } else if (NSMaxY(_scrollView.documentVisibleRect) < bottom - 0.1) {
+ NSPoint origin = _scrollView.contentView.bounds.origin;
+ origin.y += bottom - NSMaxY(_scrollView.documentVisibleRect);
+ [_scrollView.contentView scrollToPoint:origin];
+ _scrollView.verticalScroller.doubleValue =
+ NSMinY(_scrollView.documentVisibleRect) / _clippedHeight;
+ }
}
_hilitedIndex = hilitedIndex;
}
@@ -2934,45 +3077,77 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton {
for (SquirrelIndex index :
(SquirrelIndex[2]){_functionButton, functionButton}) {
switch (index) {
+ case kBackSpaceKey:
+ case kEscapeKey:
+ self.needsDisplayInRect = _deleteBackRect;
+ [_preeditView setNeedsDisplayInRect:[self convertRect:_deleteBackRect
+ toView:_preeditView]
+ avoidAdditionalLayout:YES];
+ break;
case kPageUpKey:
case kHomeKey:
self.needsDisplayInRect = _pageUpRect;
- [_textView setNeedsDisplayInRect:[self convertRect:_pageUpRect
- toView:_textView]
- avoidAdditionalLayout:YES];
+ [_pagingView setNeedsDisplayInRect:[self convertRect:_pageUpRect
+ toView:_pagingView]
+ avoidAdditionalLayout:YES];
break;
case kPageDownKey:
case kEndKey:
self.needsDisplayInRect = _pageDownRect;
- [_textView setNeedsDisplayInRect:[self convertRect:_pageDownRect
- toView:_textView]
- avoidAdditionalLayout:YES];
- break;
- case kBackSpaceKey:
- case kEscapeKey:
- self.needsDisplayInRect = _deleteBackRect;
- [_textView setNeedsDisplayInRect:[self convertRect:_deleteBackRect
- toView:_textView]
- avoidAdditionalLayout:YES];
+ [_pagingView setNeedsDisplayInRect:[self convertRect:_pageDownRect
+ toView:_pagingView]
+ avoidAdditionalLayout:YES];
break;
case kExpandButton:
case kCompressButton:
case kLockButton:
self.needsDisplayInRect = _expanderRect;
- [_textView setNeedsDisplayInRect:[self convertRect:_expanderRect
- toView:_textView]
- avoidAdditionalLayout:YES];
+ [_pagingView setNeedsDisplayInRect:[self convertRect:_expanderRect
+ toView:_pagingView]
+ avoidAdditionalLayout:YES];
+ break;
+ default:
break;
}
}
_functionButton = functionButton;
}
-// Bezier cubic curve, which has continuous roundness
+static NSBezierPath* squirclePath(NSRect rect, CGFloat cornerRadius) {
+ NSPoint vertices[4];
+ rectVertices(rect, vertices);
+ return squirclePath(vertices, 4, cornerRadius);
+}
+
+static NSBezierPath* squirclePath(SquirrelTextPolygon polygon,
+ CGFloat cornerRadius) {
+ NSBezierPath* path;
+ if (NSIsEmptyRect(polygon.body) && !NSIsEmptyRect(polygon.head) &&
+ !NSIsEmptyRect(polygon.tail) &&
+ NSMaxX(polygon.tail) < NSMinX(polygon.head)) {
+ NSPoint headVertices[4], tailVertices[4];
+ rectVertices(polygon.head, headVertices);
+ rectVertices(polygon.tail, tailVertices);
+ path = squirclePath(headVertices, 4, cornerRadius);
+ [path appendBezierPath:squirclePath(tailVertices, 4, cornerRadius)];
+ } else {
+ NSUInteger numVert = clamp((NSIsEmptyRect(polygon.head) ? 0 : 4UL) +
+ (NSIsEmptyRect(polygon.body) ? 0 : 2UL) +
+ (NSIsEmptyRect(polygon.tail) ? 0 : 4UL),
+ 4UL, 8UL);
+ NSPoint vertices[numVert];
+ textPolygonVertices(polygon, vertices);
+ path = squirclePath(vertices, numVert, cornerRadius);
+ }
+ return path;
+}
+
+// Bezier squircle curves, whose rounded corners are smooth (continously
+// differentiable)
static NSBezierPath* squirclePath(NSPointArray vertices,
- NSInteger numVert,
+ NSUInteger numVert,
CGFloat radius) {
- if (vertices == NULL) {
+ if (vertices == NULL || numVert < 4) {
return nil;
}
NSBezierPath* path = NSBezierPath.bezierPath;
@@ -2992,7 +3167,7 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton {
endPoint = NSMakePoint(nextPoint.x, point.y + nextDiff.dy * 0.5);
}
[path moveToPoint:endPoint];
- for (NSInteger i = 0; i < numVert; ++i) {
+ for (NSUInteger i = 0; i < numVert; ++i) {
lastDiff = nextDiff;
point = nextPoint;
nextPoint = vertices[(i + 1) % numVert];
@@ -3041,70 +3216,70 @@ static void rectVertices(NSRect rect, NSPointArray vertices) {
static void textPolygonVertices(SquirrelTextPolygon textPolygon,
NSPointArray vertices) {
- switch ((NSIsEmptyRect(textPolygon.leading) << 2) |
+ switch ((NSIsEmptyRect(textPolygon.head) << 2) |
(NSIsEmptyRect(textPolygon.body) << 1) |
- (NSIsEmptyRect(textPolygon.trailing) << 0)) {
+ (NSIsEmptyRect(textPolygon.tail) << 0)) {
case 0b011:
- rectVertices(textPolygon.leading, vertices);
+ rectVertices(textPolygon.head, vertices);
break;
case 0b110:
- rectVertices(textPolygon.trailing, vertices);
+ rectVertices(textPolygon.tail, vertices);
break;
case 0b101:
rectVertices(textPolygon.body, vertices);
break;
case 0b001: {
- NSPoint leadingVertices[4], bodyVertices[4];
- rectVertices(textPolygon.leading, leadingVertices);
+ NSPoint headVertices[4], bodyVertices[4];
+ rectVertices(textPolygon.head, headVertices);
rectVertices(textPolygon.body, bodyVertices);
- vertices[0] = leadingVertices[0];
- vertices[1] = leadingVertices[1];
+ vertices[0] = headVertices[0];
+ vertices[1] = headVertices[1];
vertices[2] = bodyVertices[0];
vertices[3] = bodyVertices[1];
vertices[4] = bodyVertices[2];
- vertices[5] = leadingVertices[3];
+ vertices[5] = headVertices[3];
} break;
case 0b100: {
- NSPoint bodyVertices[4], trailingVertices[4];
+ NSPoint bodyVertices[4], tailVertices[4];
rectVertices(textPolygon.body, bodyVertices);
- rectVertices(textPolygon.trailing, trailingVertices);
+ rectVertices(textPolygon.tail, tailVertices);
vertices[0] = bodyVertices[0];
- vertices[1] = trailingVertices[1];
- vertices[2] = trailingVertices[2];
- vertices[3] = trailingVertices[3];
+ vertices[1] = tailVertices[1];
+ vertices[2] = tailVertices[2];
+ vertices[3] = tailVertices[3];
vertices[4] = bodyVertices[2];
vertices[5] = bodyVertices[3];
} break;
case 0b010:
- if (NSMinX(textPolygon.leading) <= NSMaxX(textPolygon.trailing)) {
- NSPoint leadingVertices[4], trailingVertices[4];
- rectVertices(textPolygon.leading, leadingVertices);
- rectVertices(textPolygon.trailing, trailingVertices);
- vertices[0] = leadingVertices[0];
- vertices[1] = leadingVertices[1];
- vertices[2] = trailingVertices[0];
- vertices[3] = trailingVertices[1];
- vertices[4] = trailingVertices[2];
- vertices[5] = trailingVertices[3];
- vertices[6] = leadingVertices[2];
- vertices[7] = leadingVertices[3];
+ if (NSMinX(textPolygon.head) <= NSMaxX(textPolygon.tail)) {
+ NSPoint headVertices[4], tailVertices[4];
+ rectVertices(textPolygon.head, headVertices);
+ rectVertices(textPolygon.tail, tailVertices);
+ vertices[0] = headVertices[0];
+ vertices[1] = headVertices[1];
+ vertices[2] = tailVertices[0];
+ vertices[3] = tailVertices[1];
+ vertices[4] = tailVertices[2];
+ vertices[5] = tailVertices[3];
+ vertices[6] = headVertices[2];
+ vertices[7] = headVertices[3];
} else {
vertices = NULL;
}
break;
case 0b000: {
- NSPoint leadingVertices[4], bodyVertices[4], trailingVertices[4];
- rectVertices(textPolygon.leading, leadingVertices);
+ NSPoint headVertices[4], bodyVertices[4], tailVertices[4];
+ rectVertices(textPolygon.head, headVertices);
rectVertices(textPolygon.body, bodyVertices);
- rectVertices(textPolygon.trailing, trailingVertices);
- vertices[0] = leadingVertices[0];
- vertices[1] = leadingVertices[1];
+ rectVertices(textPolygon.tail, tailVertices);
+ vertices[0] = headVertices[0];
+ vertices[1] = headVertices[1];
vertices[2] = bodyVertices[0];
- vertices[3] = trailingVertices[1];
- vertices[4] = trailingVertices[2];
- vertices[5] = trailingVertices[3];
+ vertices[3] = tailVertices[1];
+ vertices[4] = tailVertices[2];
+ vertices[5] = tailVertices[3];
vertices[6] = bodyVertices[2];
- vertices[7] = leadingVertices[3];
+ vertices[7] = headVertices[3];
} break;
default:
vertices = NULL;
@@ -3153,9 +3328,7 @@ - (CAShapeLayer*)getFunctionButtonLayer {
if (!NSIsEmptyRect(buttonRect) && buttonColor) {
CGFloat cornerRadius =
fmin(_currentTheme.hilitedCornerRadius, NSHeight(buttonRect) * 0.5);
- NSPoint buttonVertices[4];
- rectVertices(buttonRect, buttonVertices);
- NSBezierPath* buttonPath = squirclePath(buttonVertices, 4, cornerRadius);
+ NSBezierPath* buttonPath = squirclePath(buttonRect, cornerRadius);
CAShapeLayer* functionButtonLayer = CAShapeLayer.alloc.init;
functionButtonLayer.path = buttonPath.quartzPath;
functionButtonLayer.fillColor = buttonColor.CGColor;
@@ -3173,421 +3346,286 @@ - (void)updateLayer {
backgroundRect = [self backingAlignedRect:backgroundRect
options:NSAlignAllEdgesNearest];
- NSRange visibleRange;
- if (@available(macOS 12.0, *)) {
- visibleRange =
- [self getCharRangeFromTextRange:_textView.textLayoutManager
- .textViewportLayoutController
- .viewportRange];
- } else {
- NSRange containerGlyphRange = NSMakeRange(NSNotFound, 0);
- [_textView.layoutManager textContainerForGlyphAtIndex:0
- effectiveRange:&containerGlyphRange];
- visibleRange =
- [_textView.layoutManager characterRangeForGlyphRange:containerGlyphRange
- actualGlyphRange:NULL];
- }
- NSRange preeditRange = NSIntersectionRange(_preeditRange, visibleRange);
- NSRange candidateBlockRange;
- if (_candidateCount > 0) {
- NSUInteger candidateBlockLength =
- NSMaxRange(_candidateRanges[_candidateCount - 1]) -
- _candidateRanges[0].location;
- candidateBlockRange = NSIntersectionRange(
- NSMakeRange(_candidateRanges[0].location, candidateBlockLength),
- visibleRange);
- } else {
- candidateBlockRange = NSMakeRange(NSNotFound, 0);
- }
- NSRange pagingRange = NSIntersectionRange(_pagingRange, visibleRange);
-
- // Draw preedit Rect
- _preeditBlock = NSZeroRect;
+ /*** Preedit Rects **/
_deleteBackRect = NSZeroRect;
NSBezierPath* hilitedPreeditPath;
- if (preeditRange.length > 0) {
- NSRect innerBox = [self blockRectForRange:preeditRange];
- _preeditBlock = NSMakeRect(
- backgroundRect.origin.x, backgroundRect.origin.y,
- backgroundRect.size.width,
- innerBox.size.height +
- (candidateBlockRange.length > 0 ? theme.preeditLinespace : 0.0));
- _preeditBlock = [self backingAlignedRect:_preeditBlock
- options:NSAlignAllEdgesNearest];
-
- // Draw hilited part of preedit text
- NSRange hilitedPreeditRange =
- NSIntersectionRange(_hilitedPreeditRange, visibleRange);
+ if (!_preeditView.hidden) {
+ _preeditRect.size.width = NSWidth(backgroundRect);
+ _preeditRect = [self backingAlignedRect:_preeditRect
+ options:NSAlignAllEdgesNearest];
+ // Draw the highlighted part of preedit text
CGFloat cornerRadius =
fmin(theme.hilitedCornerRadius,
theme.preeditParagraphStyle.minimumLineHeight * 0.5);
- if (hilitedPreeditRange.length > 0 && theme.hilitedPreeditBackColor) {
+ if (_hilitedPreeditRange.length > 0 && theme.hilitedPreeditBackColor) {
CGFloat padding =
ceil(theme.preeditParagraphStyle.minimumLineHeight * 0.05);
- innerBox.origin.x += _marginInsets.left - padding;
+ NSRect innerBox = _preeditRect;
+ innerBox.origin.x += ceil(theme.fullWidth * 0.5) - padding;
innerBox.size.width =
- backgroundRect.size.width - theme.fullWidth + padding * 2;
- innerBox.origin.y += _marginInsets.top;
+ NSWidth(backgroundRect) - theme.fullWidth + padding * 2;
innerBox = [self backingAlignedRect:innerBox
options:NSAlignAllEdgesNearest];
SquirrelTextPolygon textPolygon =
- [self textPolygonForRange:hilitedPreeditRange];
- NSInteger numVert = 0;
- if (!NSIsEmptyRect(textPolygon.leading)) {
- textPolygon.leading.origin.x += _marginInsets.left - padding;
- textPolygon.leading.origin.y += _marginInsets.top;
- textPolygon.leading.size.width += padding * 2;
- textPolygon.leading = [self
- backingAlignedRect:NSIntersectionRect(textPolygon.leading, innerBox)
+ [self textPolygonForRange:_hilitedPreeditRange inView:_preeditView];
+ if (!NSIsEmptyRect(textPolygon.head)) {
+ textPolygon.head.origin.x +=
+ theme.borderInsets.width + ceil(theme.fullWidth * 0.5) - padding;
+ textPolygon.head.origin.y += theme.borderInsets.height;
+ textPolygon.head.size.width += padding * 2;
+ textPolygon.head = [self
+ backingAlignedRect:NSIntersectionRect(textPolygon.head, innerBox)
options:NSAlignAllEdgesNearest];
- numVert += 4;
}
if (!NSIsEmptyRect(textPolygon.body)) {
- textPolygon.body.origin.x += _marginInsets.left - padding;
- textPolygon.body.origin.y += _marginInsets.top;
+ textPolygon.body.origin.x +=
+ theme.borderInsets.width + ceil(theme.fullWidth * 0.5) - padding;
+ textPolygon.body.origin.y += theme.borderInsets.height;
textPolygon.body.size.width += padding;
- if (!NSIsEmptyRect(textPolygon.trailing) ||
- NSMaxRange(hilitedPreeditRange) + 2 == NSMaxRange(preeditRange)) {
+ if (!NSIsEmptyRect(textPolygon.tail) ||
+ NSMaxRange(_hilitedPreeditRange) + 2 == _preeditContents.length) {
textPolygon.body.size.width += padding;
}
textPolygon.body = [self
backingAlignedRect:NSIntersectionRect(textPolygon.body, innerBox)
options:NSAlignAllEdgesNearest];
- numVert += 2;
}
- if (!NSIsEmptyRect(textPolygon.trailing)) {
- textPolygon.trailing.origin.x += _marginInsets.left - padding;
- textPolygon.trailing.origin.y += _marginInsets.top;
- textPolygon.trailing.size.width += padding;
- if (NSMaxRange(hilitedPreeditRange) + 2 == NSMaxRange(preeditRange)) {
- textPolygon.trailing.size.width += padding;
+ if (!NSIsEmptyRect(textPolygon.tail)) {
+ textPolygon.tail.origin.x +=
+ theme.borderInsets.width + ceil(theme.fullWidth * 0.5) - padding;
+ textPolygon.tail.origin.y += theme.borderInsets.height;
+ textPolygon.tail.size.width += padding;
+ if (NSMaxRange(_hilitedPreeditRange) + 2 == _preeditContents.length) {
+ textPolygon.tail.size.width += padding;
}
- textPolygon.trailing =
- [self backingAlignedRect:NSIntersectionRect(textPolygon.trailing,
- innerBox)
- options:NSAlignAllEdgesNearest];
- numVert += 4;
- }
-
- // Handles the special case where containing boxes are separated
- if (NSIsEmptyRect(textPolygon.body) &&
- !NSIsEmptyRect(textPolygon.leading) &&
- !NSIsEmptyRect(textPolygon.trailing) &&
- NSMaxX(textPolygon.trailing) < NSMinX(textPolygon.leading)) {
- NSPoint leadingVertices[4], trailingVertices[4];
- rectVertices(textPolygon.leading, leadingVertices);
- rectVertices(textPolygon.trailing, trailingVertices);
- hilitedPreeditPath = squirclePath(leadingVertices, 4, cornerRadius);
- [hilitedPreeditPath
- appendBezierPath:squirclePath(trailingVertices, 4, cornerRadius)];
- } else {
- numVert = numVert > 8 ? 8 : numVert < 4 ? 4 : numVert;
- NSPoint polygonVertices[numVert];
- textPolygonVertices(textPolygon, polygonVertices);
- hilitedPreeditPath =
- squirclePath(polygonVertices, numVert, cornerRadius);
+ textPolygon.tail = [self
+ backingAlignedRect:NSIntersectionRect(textPolygon.tail, innerBox)
+ options:NSAlignAllEdgesNearest];
}
+ hilitedPreeditPath = squirclePath(textPolygon, cornerRadius);
}
_deleteBackRect =
- [self blockRectForRange:NSMakeRange(NSMaxRange(preeditRange) - 1, 1)];
- _deleteBackRect.size.width += floor(theme.fullWidth * 0.5);
+ [self blockRectForRange:NSMakeRange(_preeditContents.length - 1, 1)
+ inView:_preeditView];
+ _deleteBackRect.size.width += theme.fullWidth;
_deleteBackRect.origin.x =
NSMaxX(backgroundRect) - NSWidth(_deleteBackRect);
- _deleteBackRect.origin.y += _marginInsets.top;
+ _deleteBackRect.origin.y += theme.borderInsets.height;
_deleteBackRect = [self
- backingAlignedRect:NSIntersectionRect(_deleteBackRect, _preeditBlock)
+ backingAlignedRect:NSIntersectionRect(_deleteBackRect, _preeditRect)
options:NSAlignAllEdgesNearest];
}
- // Draw candidate Rect
- _candidateBlock = NSZeroRect;
+ /*** Candidates Rects, all in documentView coordinates (except for
+ * `candidatesRect`) ***/
_candidatePolygons = NULL;
_sectionRects = NULL;
_tabularIndices = NULL;
NSBezierPath *candidateBlockPath, *hilitedCandidatePath;
NSBezierPath *gridPath, *activePagePath;
- if (candidateBlockRange.length > 0) {
- _candidateBlock = [self blockRectForRange:candidateBlockRange];
- _candidateBlock.size.width = backgroundRect.size.width;
- _candidateBlock.origin.x = backgroundRect.origin.x;
- _candidateBlock.origin.y = preeditRange.length == 0 ? NSMinY(backgroundRect)
- : NSMaxY(_preeditBlock);
- if (pagingRange.length == 0) {
- _candidateBlock.size.height =
- NSMaxY(backgroundRect) - NSMinY(_candidateBlock);
- } else if (!theme.linear) {
- _candidateBlock.size.height += theme.linespace;
- }
- _candidateBlock = [self
- backingAlignedRect:NSIntersectionRect(_candidateBlock, backgroundRect)
+ if (!_scrollView.hidden) {
+ _candidatesRect.size.width = NSWidth(backgroundRect);
+ _candidatesRect = [self
+ backingAlignedRect:NSIntersectionRect(_candidatesRect, backgroundRect)
options:NSAlignAllEdgesNearest];
+ _documentRect.size.width = NSWidth(backgroundRect);
NSPoint candidateBlockVertices[4];
- rectVertices(_candidateBlock, candidateBlockVertices);
+ rectVertices(_candidatesRect, candidateBlockVertices);
CGFloat blockCornerRadius =
- fmin(theme.hilitedCornerRadius, NSHeight(_candidateBlock) * 0.5);
+ fmin(theme.hilitedCornerRadius, NSHeight(_candidatesRect) * 0.5);
candidateBlockPath =
squirclePath(candidateBlockVertices, 4, blockCornerRadius);
-
- // Draw candidate highlight rect
+ // Store candidate enclosing polygons and draw the ones highlighted
CGFloat cornerRadius =
fmin(theme.hilitedCornerRadius,
theme.candidateParagraphStyle.minimumLineHeight * 0.5);
_candidatePolygons = new SquirrelTextPolygon[_candidateCount];
- if (theme.linear) {
+ if (theme.linear) { // linear layout
CGFloat gridOriginY;
CGFloat tabInterval;
NSUInteger lineNum = 0;
- NSRect sectionRect = _candidateBlock;
+ NSRect sectionRect = _documentRect;
if (theme.tabular) {
_tabularIndices = new SquirrelTabularIndex[_candidateCount];
_sectionRects = new NSRect[_candidateCount / theme.pageSize];
gridPath = NSBezierPath.bezierPath;
- gridOriginY = NSMinY(_candidateBlock);
+ gridOriginY = NSMinY(_documentRect);
tabInterval = theme.fullWidth * 2;
sectionRect.size.height = 0;
}
for (NSUInteger i = 0; i < _candidateCount; ++i) {
- NSRange candidateRange =
- NSIntersectionRange(NSMakeRange(_candidateRanges[i].location,
- _candidateRanges[i].length),
- visibleRange);
- if (candidateRange.length == 0) {
- _candidateCount = i;
- break;
- }
+ NSRange candidateRange = RangeCandidate(_candidateRanges[i]);
SquirrelTextPolygon candidatePolygon =
- [self textPolygonForRange:candidateRange];
- if (!NSIsEmptyRect(candidatePolygon.leading)) {
- candidatePolygon.leading.origin.x += theme.borderInsets.width;
- candidatePolygon.leading.size.width += theme.fullWidth;
- candidatePolygon.leading.origin.y += _marginInsets.top;
- candidatePolygon.leading = [self
- backingAlignedRect:NSIntersectionRect(candidatePolygon.leading,
- _candidateBlock)
- options:NSAlignAllEdgesNearest];
+ [self textPolygonForRange:candidateRange inView:_textView];
+ if (!NSIsEmptyRect(candidatePolygon.head)) {
+ candidatePolygon.head.size.width += theme.fullWidth;
+ candidatePolygon.head =
+ [_documentView backingAlignedRect:candidatePolygon.head
+ options:NSAlignAllEdgesNearest];
}
- if (!NSIsEmptyRect(candidatePolygon.trailing)) {
- candidatePolygon.trailing.origin.x += theme.borderInsets.width;
- candidatePolygon.trailing.origin.y += _marginInsets.top;
- candidatePolygon.trailing = [self
- backingAlignedRect:NSIntersectionRect(candidatePolygon.trailing,
- _candidateBlock)
- options:NSAlignAllEdgesNearest];
+ if (!NSIsEmptyRect(candidatePolygon.tail)) {
+ candidatePolygon.tail =
+ [_documentView backingAlignedRect:candidatePolygon.tail
+ options:NSAlignAllEdgesNearest];
}
if (!NSIsEmptyRect(candidatePolygon.body)) {
- candidatePolygon.body.origin.x += theme.borderInsets.width;
if (_truncated[i]) {
- candidatePolygon.body.size.width =
- NSMaxX(_candidateBlock) - NSMinX(candidatePolygon.body);
- } else if (!NSIsEmptyRect(candidatePolygon.trailing)) {
+ candidatePolygon.body.size.width = NSWidth(_documentRect);
+ } else if (!NSIsEmptyRect(candidatePolygon.tail)) {
candidatePolygon.body.size.width += theme.fullWidth;
}
- candidatePolygon.body.origin.y += _marginInsets.top;
candidatePolygon.body =
- [self backingAlignedRect:NSIntersectionRect(candidatePolygon.body,
- _candidateBlock)
- options:NSAlignAllEdgesNearest];
+ [_documentView backingAlignedRect:candidatePolygon.body
+ options:NSAlignAllEdgesNearest];
}
if (theme.tabular) {
if (_expanded) {
if (i % theme.pageSize == 0) {
- sectionRect.origin.y += NSHeight(sectionRect);
+ sectionRect.origin.y = ceil(NSMaxY(sectionRect));
} else if (i % theme.pageSize == theme.pageSize - 1) {
sectionRect.size.height =
- NSMaxY(NSIsEmptyRect(candidatePolygon.trailing)
+ NSMaxY(NSIsEmptyRect(candidatePolygon.tail)
? candidatePolygon.body
- : candidatePolygon.trailing) -
+ : candidatePolygon.tail) -
NSMinY(sectionRect);
NSUInteger sec = i / theme.pageSize;
_sectionRects[sec] = sectionRect;
if (sec == _hilitedIndex / theme.pageSize) {
- NSPoint activePageVertices[4];
- rectVertices(sectionRect, activePageVertices);
CGFloat pageCornerRadius = fmin(theme.hilitedCornerRadius,
NSHeight(sectionRect) * 0.5);
- activePagePath =
- squirclePath(activePageVertices, 4, pageCornerRadius);
+ activePagePath = squirclePath(sectionRect, pageCornerRadius);
}
}
}
- CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(candidatePolygon.trailing)
+ CGFloat bottomEdge = NSMaxY(NSIsEmptyRect(candidatePolygon.tail)
? candidatePolygon.body
- : candidatePolygon.trailing);
+ : candidatePolygon.tail);
if (fabs(bottomEdge - gridOriginY) > 2) {
lineNum += i > 0 ? 1 : 0;
// horizontal border except for the last line
- if (fabs(bottomEdge - NSMaxY(_candidateBlock)) > 2) {
- [gridPath moveToPoint:NSMakePoint(NSMinX(_candidateBlock) +
- ceil(theme.fullWidth * 0.5),
+ if (bottomEdge < NSMaxY(_documentRect) - 2) {
+ [gridPath moveToPoint:NSMakePoint(ceil(theme.fullWidth * 0.5),
bottomEdge)];
[gridPath
- lineToPoint:NSMakePoint(NSMaxX(_candidateBlock) -
+ lineToPoint:NSMakePoint(NSMaxX(_documentRect) -
floor(theme.fullWidth * 0.5),
bottomEdge)];
}
gridOriginY = bottomEdge;
}
- NSPoint headOrigin = (NSIsEmptyRect(candidatePolygon.leading)
- ? candidatePolygon.body
- : candidatePolygon.leading)
- .origin;
- NSUInteger headTabColumn = (NSUInteger)round(
- (headOrigin.x - _marginInsets.left) / tabInterval);
+ NSPoint leadOrigin =
+ (NSIsEmptyRect(candidatePolygon.head) ? candidatePolygon.body
+ : candidatePolygon.head)
+ .origin;
+ NSUInteger leadTabColumn =
+ (NSUInteger)round(leadOrigin.x / tabInterval);
// vertical bar
- if (headOrigin.x > NSMinX(_candidateBlock) + theme.fullWidth) {
+ if (leadOrigin.x > NSMinX(_documentRect) + theme.fullWidth) {
[gridPath
- moveToPoint:NSMakePoint(headOrigin.x,
- headOrigin.y + cornerRadius * 0.8)];
+ moveToPoint:NSMakePoint(leadOrigin.x,
+ leadOrigin.y + cornerRadius * 0.8)];
[gridPath
lineToPoint:NSMakePoint(
- headOrigin.x,
- NSMaxY(NSIsEmptyRect(candidatePolygon.leading)
+ leadOrigin.x,
+ NSMaxY(NSIsEmptyRect(candidatePolygon.head)
? candidatePolygon.body
- : candidatePolygon.leading) -
+ : candidatePolygon.head) -
cornerRadius * 0.8)];
}
_tabularIndices[i] = (SquirrelTabularIndex){
- .index = i, .lineNum = lineNum, .tabNum = headTabColumn};
+ .index = i, .lineNum = lineNum, .tabNum = leadTabColumn};
}
_candidatePolygons[i] = candidatePolygon;
}
- if (_hilitedIndex < _candidateCount) {
- NSInteger numVert =
- (NSIsEmptyRect(_candidatePolygons[_hilitedIndex].leading) ? 0 : 4) +
- (NSIsEmptyRect(_candidatePolygons[_hilitedIndex].body) ? 0 : 2) +
- (NSIsEmptyRect(_candidatePolygons[_hilitedIndex].trailing) ? 0 : 4);
- // Handles the special case where containing boxes are separated
- if (numVert == 8 &&
- NSMaxX(_candidatePolygons[_hilitedIndex].trailing) <
- NSMinX(_candidatePolygons[_hilitedIndex].leading)) {
- NSPoint leadingVertices[4], trailingVertices[4];
- rectVertices(_candidatePolygons[_hilitedIndex].leading,
- leadingVertices);
- rectVertices(_candidatePolygons[_hilitedIndex].trailing,
- trailingVertices);
- hilitedCandidatePath = squirclePath(leadingVertices, 4, cornerRadius);
- [hilitedCandidatePath
- appendBezierPath:squirclePath(trailingVertices, 4, cornerRadius)];
- } else {
- numVert = numVert > 8 ? 8 : numVert < 4 ? 4 : numVert;
- NSPoint polygonVertices[numVert];
- textPolygonVertices(_candidatePolygons[_hilitedIndex],
- polygonVertices);
- hilitedCandidatePath =
- squirclePath(polygonVertices, numVert, cornerRadius);
- }
- }
+ hilitedCandidatePath =
+ squirclePath(_candidatePolygons[_hilitedIndex], cornerRadius);
} else { // stacked layout
for (NSUInteger i = 0; i < _candidateCount; ++i) {
- NSRange candidateRange =
- NSIntersectionRange(NSMakeRange(_candidateRanges[i].location,
- _candidateRanges[i].length),
- visibleRange);
- candidateRange = NSIntersectionRange(candidateRange, visibleRange);
- if (candidateRange.length == 0) {
- _candidateCount = i;
- break;
- }
- NSRect candidateRect = [self blockRectForRange:candidateRange];
- candidateRect.size.width = backgroundRect.size.width;
- candidateRect.origin.x = backgroundRect.origin.x;
- candidateRect.origin.y +=
- _marginInsets.top - ceil(theme.linespace * 0.5);
+ NSRange candidateRange = RangeCandidate(_candidateRanges[i]);
+ NSRect candidateRect = [self blockRectForRange:candidateRange
+ inView:_textView];
+ candidateRect.size.width = NSWidth(_documentRect);
candidateRect.size.height += theme.linespace;
candidateRect =
- [self backingAlignedRect:NSIntersectionRect(candidateRect,
- _candidateBlock)
- options:NSAlignAllEdgesNearest];
+ [_documentView backingAlignedRect:candidateRect
+ options:NSAlignAllEdgesNearest];
_candidatePolygons[i] =
(SquirrelTextPolygon){NSZeroRect, candidateRect, NSZeroRect};
}
- if (_hilitedIndex < _candidateCount) {
- NSPoint candidateVertices[4];
- rectVertices(_candidatePolygons[_hilitedIndex].body, candidateVertices);
- hilitedCandidatePath = squirclePath(candidateVertices, 4, cornerRadius);
- }
+ hilitedCandidatePath =
+ squirclePath(_candidatePolygons[_hilitedIndex].body, cornerRadius);
}
}
- // Draw paging Rect
- _pagingBlock = NSZeroRect;
+ /*** Paging Rects ***/
_pageUpRect = NSZeroRect;
_pageDownRect = NSZeroRect;
_expanderRect = NSZeroRect;
- if (pagingRange.length > 0) {
+ if (!_pagingView.hidden) {
if (theme.linear) {
- _pagingBlock = [self blockRectForRange:pagingRange];
- _pagingBlock.size.width += theme.fullWidth;
- _pagingBlock.origin.x = NSMaxX(backgroundRect) - NSWidth(_pagingBlock);
+ _pagingRect.origin.x = NSMaxX(backgroundRect) - NSWidth(_pagingRect);
} else {
- _pagingBlock = backgroundRect;
+ _pagingRect.size.width = NSWidth(backgroundRect);
}
- _pagingBlock.origin.y = NSMaxY(_candidateBlock);
- _pagingBlock.size.height = NSMaxY(backgroundRect) - NSMaxY(_candidateBlock);
if (theme.showPaging) {
- _pageUpRect =
- [self blockRectForRange:NSMakeRange(pagingRange.location, 1)];
+ _pageUpRect = [self blockRectForRange:NSMakeRange(0, 1)
+ inView:_pagingView];
_pageDownRect =
- [self blockRectForRange:NSMakeRange(NSMaxRange(pagingRange) - 1, 1)];
- _pageDownRect.origin.x += _marginInsets.left;
- _pageDownRect.size.width += ceil(theme.fullWidth * 0.5);
- _pageDownRect.origin.y += _marginInsets.top;
- _pageUpRect.origin.x += theme.borderInsets.width;
+ [self blockRectForRange:NSMakeRange(_pagingContents.length - 1, 1)
+ inView:_pagingView];
+ _pageDownRect.origin.x += NSMinX(_pagingRect);
+ _pageDownRect.size.width += theme.fullWidth;
+ _pageDownRect.origin.y += NSMinY(_pagingRect);
+ _pageUpRect.origin.x += NSMinX(_pagingRect);
// bypass the bug of getting wrong glyph position when tab is presented
_pageUpRect.size.width = NSWidth(_pageDownRect);
- _pageUpRect.origin.y += _marginInsets.top;
+ _pageUpRect.origin.y += NSMinY(_pagingRect);
_pageUpRect =
- [self backingAlignedRect:NSIntersectionRect(_pageUpRect, _pagingBlock)
+ [self backingAlignedRect:NSIntersectionRect(_pageUpRect, _pagingRect)
options:NSAlignAllEdgesNearest];
_pageDownRect = [self
- backingAlignedRect:NSIntersectionRect(_pageDownRect, _pagingBlock)
+ backingAlignedRect:NSIntersectionRect(_pageDownRect, _pagingRect)
options:NSAlignAllEdgesNearest];
}
if (theme.tabular) {
_expanderRect =
- [self blockRectForRange:NSMakeRange(pagingRange.location +
- pagingRange.length / 2,
- 1)];
- _expanderRect.origin.x += theme.borderInsets.width;
+ [self blockRectForRange:NSMakeRange(_pagingContents.length / 2, 1)
+ inView:_pagingView];
+ _expanderRect.origin.x += NSMinX(_pagingRect);
_expanderRect.size.width += theme.fullWidth;
- _expanderRect.origin.y += _marginInsets.top;
+ _expanderRect.origin.y += NSMinY(_pagingRect);
_expanderRect = [self
- backingAlignedRect:NSIntersectionRect(_expanderRect, backgroundRect)
+ backingAlignedRect:NSIntersectionRect(_expanderRect, _pagingRect)
options:NSAlignAllEdgesNearest];
}
}
- // Draw borders
+ /*** Border Rects ***/
CGFloat outerCornerRadius =
fmin(theme.cornerRadius, NSHeight(panelRect) * 0.5);
CGFloat innerCornerRadius =
- fmax(fmin(theme.hilitedCornerRadius, NSHeight(backgroundRect) * 0.5),
- outerCornerRadius -
- fmin(theme.borderInsets.width, theme.borderInsets.height));
+ clamp(theme.hilitedCornerRadius,
+ outerCornerRadius -
+ fmin(theme.borderInsets.width, theme.borderInsets.height),
+ NSHeight(backgroundRect) * 0.5);
NSBezierPath *panelPath, *backgroundPath;
- if (!theme.linear || pagingRange.length == 0) {
- NSPoint panelVertices[4], backgroundVertices[4];
- rectVertices(panelRect, panelVertices);
- rectVertices(backgroundRect, backgroundVertices);
- panelPath = squirclePath(panelVertices, 4, outerCornerRadius);
- backgroundPath = squirclePath(backgroundVertices, 4, innerCornerRadius);
+ if (!theme.linear || _pagingView.hidden) {
+ panelPath = squirclePath(panelRect, outerCornerRadius);
+ backgroundPath = squirclePath(backgroundRect, innerCornerRadius);
} else {
- NSPoint panelVertices[6], backgroundVertices[6];
NSRect mainPanelRect = panelRect;
- mainPanelRect.size.height -= NSHeight(_pagingBlock);
+ mainPanelRect.size.height -= NSHeight(_pagingRect);
NSRect tailPanelRect =
- NSInsetRect(NSOffsetRect(_pagingBlock, 0, theme.borderInsets.height),
+ NSInsetRect(NSOffsetRect(_pagingRect, 0, theme.borderInsets.height),
-theme.borderInsets.width, 0);
- textPolygonVertices(
+ panelPath = squirclePath(
(SquirrelTextPolygon){mainPanelRect, tailPanelRect, NSZeroRect},
- panelVertices);
- panelPath = squirclePath(panelVertices, 6, outerCornerRadius);
+ outerCornerRadius);
NSRect mainBackgroundRect = backgroundRect;
- mainBackgroundRect.size.height -= NSHeight(_pagingBlock);
- textPolygonVertices(
- (SquirrelTextPolygon){mainBackgroundRect, _pagingBlock, NSZeroRect},
- backgroundVertices);
- backgroundPath = squirclePath(backgroundVertices, 6, innerCornerRadius);
+ mainBackgroundRect.size.height -= NSHeight(_pagingRect);
+ backgroundPath = squirclePath(
+ (SquirrelTextPolygon){mainBackgroundRect, _pagingRect, NSZeroRect},
+ innerCornerRadius);
}
NSBezierPath* borderPath = panelPath.copy;
[borderPath appendBezierPath:backgroundPath];
@@ -3597,7 +3635,7 @@ - (void)updateLayer {
[flip scaleXBy:1 yBy:-1];
NSBezierPath* shapePath = [flip transformBezierPath:panelPath];
- // Set layers
+ /*** Draw into layers ***/
_shape.path = shapePath.quartzPath;
_shape.fillColor = NSColor.whiteColor.CGColor;
self.layer.sublayers = nil;
@@ -3608,7 +3646,7 @@ - (void)updateLayer {
shapeLayer.fillColor = NSColor.whiteColor.CGColor;
BackLayers.mask = shapeLayer;
if (@available(macOS 10.14, *)) {
- BackLayers.opacity = 1.0f - (float)theme.translucency;
+ BackLayers.opacity = 1.0f - theme.translucency;
BackLayers.allowsGroupOpacity = YES;
}
[self.layer addSublayer:BackLayers];
@@ -3630,16 +3668,14 @@ - (void)updateLayer {
}
// background color layer
CAShapeLayer* backColorLayer = CAShapeLayer.alloc.init;
- if ((!NSIsEmptyRect(_preeditBlock) || !NSIsEmptyRect(_pagingBlock) ||
+ if ((!NSIsEmptyRect(_preeditRect) || !NSIsEmptyRect(_pagingRect) ||
!NSIsEmptyRect(_expanderRect)) &&
theme.preeditBackColor) {
- if (candidateBlockPath) {
+ if (candidateBlockPath != nil) {
NSBezierPath* nonCandidatePath = backgroundPath.copy;
[nonCandidatePath appendBezierPath:candidateBlockPath];
backColorLayer.path = nonCandidatePath.quartzPath;
backColorLayer.fillRule = kCAFillRuleEvenOdd;
- backColorLayer.strokeColor = theme.preeditBackColor.CGColor;
- backColorLayer.lineWidth = 0.5;
backColorLayer.fillColor = theme.preeditBackColor.CGColor;
[BackLayers addSublayer:backColorLayer];
// candidate block's background color layer
@@ -3649,15 +3685,11 @@ - (void)updateLayer {
[BackLayers addSublayer:candidateLayer];
} else {
backColorLayer.path = backgroundPath.quartzPath;
- backColorLayer.strokeColor = theme.preeditBackColor.CGColor;
- backColorLayer.lineWidth = 0.5;
backColorLayer.fillColor = theme.preeditBackColor.CGColor;
[BackLayers addSublayer:backColorLayer];
}
} else {
backColorLayer.path = backgroundPath.quartzPath;
- backColorLayer.strokeColor = theme.backColor.CGColor;
- backColorLayer.lineWidth = 0.5;
backColorLayer.fillColor = theme.backColor.CGColor;
[BackLayers addSublayer:backColorLayer];
}
@@ -3675,54 +3707,60 @@ - (void)updateLayer {
ForeLayers.mask = maskLayer;
[self.layer addSublayer:ForeLayers];
// highlighted preedit layer
- if (hilitedPreeditPath && theme.hilitedPreeditBackColor) {
+ if (hilitedPreeditPath != nil && theme.hilitedPreeditBackColor != nil) {
CAShapeLayer* hilitedPreeditLayer = CAShapeLayer.alloc.init;
hilitedPreeditLayer.path = hilitedPreeditPath.quartzPath;
hilitedPreeditLayer.fillColor = theme.hilitedPreeditBackColor.CGColor;
[ForeLayers addSublayer:hilitedPreeditLayer];
}
// highlighted candidate layer
- if (hilitedCandidatePath && theme.hilitedCandidateBackColor) {
- if (activePagePath) {
- CAShapeLayer* activePageLayer = CAShapeLayer.alloc.init;
- activePageLayer.path = activePagePath.quartzPath;
- activePageLayer.fillColor =
- [[theme.hilitedCandidateBackColor
- blendedColorWithFraction:0.8
- ofColor:[theme.backColor
- colorWithAlphaComponent:1.0]]
- colorWithAlphaComponent:theme.backColor.alphaComponent]
+ if (!_scrollView.hidden) {
+ _documentView.layer.sublayers = nil;
+ if (hilitedCandidatePath != nil && theme.hilitedCandidateBackColor != nil) {
+ if (activePagePath != nil) {
+ CAShapeLayer* activePageLayer = CAShapeLayer.alloc.init;
+ activePageLayer.path = activePagePath.quartzPath;
+ activePageLayer.fillColor =
+ [[theme.hilitedCandidateBackColor
+ blendedColorWithFraction:0.8
+ ofColor:[theme.backColor
+ colorWithAlphaComponent:1.0]]
+ colorWithAlphaComponent:theme.backColor.alphaComponent]
+ .CGColor;
+ if (@available(macOS 10.14, *)) {
+ activePageLayer.opacity = 1.0f - theme.translucency;
+ }
+ [_documentView.layer addSublayer:activePageLayer];
+ }
+ CAShapeLayer* hilitedCandidateLayer = CAShapeLayer.alloc.init;
+ hilitedCandidateLayer.path = hilitedCandidatePath.quartzPath;
+ hilitedCandidateLayer.fillColor = theme.hilitedCandidateBackColor.CGColor;
+ [_documentView.layer addSublayer:hilitedCandidateLayer];
+ }
+ // grids (in candidate block) layer
+ if (gridPath != nil) {
+ CAShapeLayer* gridLayer = CAShapeLayer.alloc.init;
+ gridLayer.path = gridPath.quartzPath;
+ gridLayer.lineWidth = 1.0;
+ gridLayer.strokeColor =
+ [theme.commentForeColor blendedColorWithFraction:0.8
+ ofColor:theme.backColor]
.CGColor;
- [BackLayers addSublayer:activePageLayer];
+ [_documentView.layer addSublayer:gridLayer];
}
- CAShapeLayer* hilitedCandidateLayer = CAShapeLayer.alloc.init;
- hilitedCandidateLayer.path = hilitedCandidatePath.quartzPath;
- hilitedCandidateLayer.fillColor = theme.hilitedCandidateBackColor.CGColor;
- [ForeLayers addSublayer:hilitedCandidateLayer];
+ [_documentView.layer addSublayer:_textView.layer];
}
+
// function buttons (page up, page down, backspace) layer
if (_functionButton != kVoidSymbol) {
- CAShapeLayer* functionButtonLayer = [self getFunctionButtonLayer];
- if (functionButtonLayer) {
+ if (CAShapeLayer* functionButtonLayer = [self getFunctionButtonLayer]) {
[ForeLayers addSublayer:functionButtonLayer];
}
}
- // grids (in candidate block) layer
- if (gridPath) {
- CAShapeLayer* gridLayer = CAShapeLayer.alloc.init;
- gridLayer.path = gridPath.quartzPath;
- gridLayer.lineWidth = 1.0;
- gridLayer.strokeColor =
- [theme.commentForeColor blendedColorWithFraction:0.8
- ofColor:theme.backColor]
- .CGColor;
- [ForeLayers addSublayer:gridLayer];
- }
// logo at the beginning for status message
- if (NSIsEmptyRect(_preeditBlock) && NSIsEmptyRect(_candidateBlock)) {
+ if (!_statusView.hidden) {
CALayer* logoLayer = CALayer.alloc.init;
- CGFloat height =
- [theme.statusAttrs[NSParagraphStyleAttributeName] minimumLineHeight];
+ CGFloat height = theme.statusParagraphStyle.minimumLineHeight;
NSRect logoRect = NSMakeRect(backgroundRect.origin.x,
backgroundRect.origin.y, height, height);
logoLayer.frame = [self
@@ -3742,39 +3780,43 @@ - (void)updateLayer {
}
- (SquirrelIndex)getIndexFromMouseSpot:(NSPoint)spot {
- NSPoint point = [self convertPoint:spot fromView:nil];
- if (NSMouseInRect(point, self.bounds, YES)) {
- if (NSMouseInRect(point, _preeditBlock, YES)) {
- return NSMouseInRect(point, _deleteBackRect, YES) ? kBackSpaceKey
- : kCodeInputArea;
+ if (NSMouseInRect(spot, self.bounds, YES)) {
+ if (NSMouseInRect(spot, _preeditRect, YES)) {
+ return NSMouseInRect(spot, _deleteBackRect, YES) ? kBackSpaceKey
+ : kCodeInputArea;
}
- if (NSMouseInRect(point, _expanderRect, YES)) {
+ if (NSMouseInRect(spot, _expanderRect, YES)) {
return kExpandButton;
}
- if (NSMouseInRect(point, _pageUpRect, YES)) {
+ if (NSMouseInRect(spot, _pageUpRect, YES)) {
return kPageUpKey;
}
- if (NSMouseInRect(point, _pageDownRect, YES)) {
+ if (NSMouseInRect(spot, _pageDownRect, YES)) {
return kPageDownKey;
}
- for (NSUInteger i = 0; i < _candidateCount; ++i) {
- if (NSMouseInRect(point, _candidatePolygons[i].body, YES) ||
- NSMouseInRect(point, _candidatePolygons[i].leading, YES) ||
- NSMouseInRect(point, _candidatePolygons[i].trailing, YES)) {
- return i;
+ if (NSMouseInRect(spot, _candidatesRect, YES)) {
+ spot.x += NSMinX(_scrollView.documentVisibleRect) -
+ NSMinX(_candidatesRect) - ceil(_currentTheme.fullWidth * 0.5);
+ spot.y += NSMinY(_scrollView.documentVisibleRect) -
+ NSMinY(_candidatesRect) - floor(_currentTheme.linespace * 0.5);
+ for (NSUInteger i = 0; i < _candidateCount; ++i) {
+ if (NSMouseInRect(spot, _candidatePolygons[i].body, YES) ||
+ NSMouseInRect(spot, _candidatePolygons[i].head, YES) ||
+ NSMouseInRect(spot, _candidatePolygons[i].tail, YES)) {
+ return (SquirrelIndex)i;
+ }
}
}
}
- return NSNotFound;
+ return kVoidSymbol;
}
@end // SquirrelView
/* In order to put SquirrelPanel above client app windows,
- SquirrelPanel needs to be assigned a window level higher
- than kCGHelpWindowLevelKey that the system tooltips use.
- This class makes system-alike tooltips above SquirrelPanel
- */
+ SquirrelPanel needs to be assigned a window level higher
+ than kCGHelpWindowLevelKey that the system tooltips use.
+ This class makes system-alike tooltips above SquirrelPanel */
@interface SquirrelToolTip : NSWindow
@property(nonatomic, strong, readonly, nullable, direct) NSTimer* displayTimer;
@@ -3794,11 +3836,10 @@ @implementation SquirrelToolTip {
}
- (instancetype)init {
- self = [super initWithContentRect:NSZeroRect
- styleMask:NSWindowStyleMaskNonactivatingPanel
- backing:NSBackingStoreBuffered
- defer:YES];
- if (self) {
+ if (self = [super initWithContentRect:NSZeroRect
+ styleMask:NSWindowStyleMaskNonactivatingPanel
+ backing:NSBackingStoreBuffered
+ defer:YES]) {
self.backgroundColor = NSColor.clearColor;
self.opaque = YES;
self.hasShadow = YES;
@@ -4036,9 +4077,10 @@ - (void)observeValueForKeyPath:(NSString*)keyPath
[clientAppearance bestMatchFromAppearancesWithNames:@[
NSAppearanceNameAqua, NSAppearanceNameDarkAqua
]];
- SquirrelAppear appear =
- [appearName isEqualToString:NSAppearanceNameDarkAqua] ? darkAppear
- : defaultAppear;
+ SquirrelAppearance appear =
+ [appearName isEqualToString:NSAppearanceNameDarkAqua]
+ ? kDarkAppearance
+ : kDefaultAppearance;
if (appear != _view.appear) {
_view.appear = appear;
self.appearance = [NSAppearance appearanceNamed:appearName];
@@ -4056,12 +4098,11 @@ - (void)observeValueForKeyPath:(NSString*)keyPath
}
- (instancetype)init {
- self = [super initWithContentRect:_IbeamRect
- styleMask:NSWindowStyleMaskNonactivatingPanel |
- NSWindowStyleMaskBorderless
- backing:NSBackingStoreBuffered
- defer:YES];
- if (self) {
+ if (self = [super initWithContentRect:_IbeamRect
+ styleMask:NSWindowStyleMaskNonactivatingPanel |
+ NSWindowStyleMaskBorderless
+ backing:NSBackingStoreBuffered
+ defer:YES]) {
self.level = CGWindowLevelForKey(kCGCursorWindowLevelKey) - 100;
self.hasShadow = NO;
self.opaque = NO;
@@ -4069,8 +4110,8 @@ - (instancetype)init {
self.delegate = self;
self.acceptsMouseMovedEvents = YES;
- NSView* contentView = NSView.alloc.init;
- _view = [SquirrelView.alloc initWithFrame:self.contentView.bounds];
+ NSFlippedView* contentView = NSFlippedView.alloc.init;
+ _view = SquirrelView.alloc.init;
if (@available(macOS 10.14, *)) {
_back = NSVisualEffectView.alloc.init;
_back.blendingMode = NSVisualEffectBlendingModeBehindWindow;
@@ -4082,7 +4123,10 @@ - (instancetype)init {
[contentView addSubview:_back];
}
[contentView addSubview:_view];
- [contentView addSubview:_view.textView];
+ [contentView addSubview:_view.preeditView];
+ [contentView addSubview:_view.scrollView];
+ [contentView addSubview:_view.pagingView];
+ [contentView addSubview:_view.statusView];
self.contentView = contentView;
_optionSwitcher = SquirrelOptionSwitcher.alloc.init;
@@ -4140,50 +4184,46 @@ - (NSUInteger)candidateIndexOnDirection:(SquirrelIndex)arrowKey {
// handle mouse interaction events
- (void)sendEvent:(NSEvent*)event {
SquirrelTheme* theme = _view.currentTheme;
- static SquirrelIndex cursorIndex = NSNotFound;
+ static SquirrelIndex cursorIndex = kVoidSymbol;
switch (event.type) {
case NSEventTypeLeftMouseDown:
if (event.clickCount == 1 && cursorIndex == kCodeInputArea) {
- NSPoint spot =
- [_view.textView convertPoint:self.mouseLocationOutsideOfEventStream
- fromView:nil];
+ NSPoint spot = [_view.preeditView
+ convertPoint:self.mouseLocationOutsideOfEventStream
+ fromView:nil];
NSUInteger inputIndex =
- [_view.textView characterIndexForInsertionAtPoint:spot];
+ [_view.preeditView characterIndexForInsertionAtPoint:spot];
if (inputIndex == 0) {
[_inputController performAction:kPROCESS onIndex:kHomeKey];
} else if (inputIndex < _caretPos) {
- [_inputController moveCursor:_caretPos
- toPosition:inputIndex
- inlinePreedit:NO
- inlineCandidate:NO];
- } else if (inputIndex >= _view.preeditRange.length) {
+ [_inputController moveCursor:_caretPos toPosition:inputIndex];
+ } else if (inputIndex >= _view.preeditContents.length - 2) {
[_inputController performAction:kPROCESS onIndex:kEndKey];
} else if (inputIndex > _caretPos + 1) {
- [_inputController moveCursor:_caretPos
- toPosition:inputIndex - 1
- inlinePreedit:NO
- inlineCandidate:NO];
+ [_inputController moveCursor:_caretPos toPosition:inputIndex - 1];
}
}
break;
case NSEventTypeLeftMouseUp:
- if (event.clickCount == 1 && cursorIndex != NSNotFound) {
+ if (event.clickCount == 1 && cursorIndex != kVoidSymbol) {
if (cursorIndex == _highlightedIndex) {
- [_inputController performAction:kSELECT
- onIndex:cursorIndex + _indexRange.location];
+ [_inputController
+ performAction:kSELECT
+ onIndex:(SquirrelIndex)(cursorIndex +
+ _indexRange.location)];
} else if (cursorIndex == _functionButton) {
if (cursorIndex == kExpandButton) {
if (_locked) {
self.locked = NO;
- [_view.textStorage
+ [_view.pagingContents
replaceCharactersInRange:NSMakeRange(
- _view.pagingRange.location +
- _view.pagingRange.length / 2,
+ _view.pagingContents.length / 2,
1)
withAttributedString:_view.expanded ? theme.symbolCompress
: theme.symbolExpand];
- _view.textView.needsDisplayInRect =
- [_view convertRect:_view.expanderRect toView:_view.textView];
+ _view.pagingView.needsDisplayInRect =
+ [_view convertRect:_view.expanderRect
+ toView:_view.pagingView];
} else {
self.expanded = !_view.expanded;
self.sectionNum = 0;
@@ -4194,10 +4234,12 @@ - (void)sendEvent:(NSEvent*)event {
}
break;
case NSEventTypeRightMouseUp:
- if (event.clickCount == 1 && cursorIndex != NSNotFound) {
+ if (event.clickCount == 1 && cursorIndex != kVoidSymbol) {
if (cursorIndex == _highlightedIndex) {
- [_inputController performAction:kDELETE
- onIndex:cursorIndex + _indexRange.location];
+ [_inputController
+ performAction:kDELETE
+ onIndex:(SquirrelIndex)(cursorIndex +
+ _indexRange.location)];
} else if (cursorIndex == _functionButton) {
switch (_functionButton) {
case kPageUpKey:
@@ -4208,24 +4250,21 @@ - (void)sendEvent:(NSEvent*)event {
break;
case kExpandButton:
self.locked = !_locked;
- [_view.textStorage
+ [_view.pagingContents
replaceCharactersInRange:NSMakeRange(
- _view.pagingRange.location +
- _view.pagingRange.length / 2,
+ _view.pagingContents.length / 2,
1)
withAttributedString:_locked ? theme.symbolLock
: _view.expanded
? theme.symbolCompress
: theme.symbolExpand];
- [_view.textStorage
+ [_view.pagingContents
addAttribute:NSForegroundColorAttributeName
value:theme.hilitedPreeditForeColor
- range:NSMakeRange(_view.pagingRange.location +
- _view.pagingRange.length / 2,
- 1)];
- [_view.textView
+ range:NSMakeRange(_view.pagingContents.length / 2, 1)];
+ [_view.pagingView
setNeedsDisplayInRect:[_view convertRect:_view.expanderRect
- toView:_view.textView]
+ toView:_view.pagingView]
avoidAdditionalLayout:YES];
[_inputController performAction:kPROCESS onIndex:kLockButton];
break;
@@ -4246,32 +4285,32 @@ - (void)sendEvent:(NSEvent*)event {
NSEventModifierFlagDeviceIndependentFlagsMask) ==
NSEventModifierFlagOption;
cursorIndex =
- [_view getIndexFromMouseSpot:self.mouseLocationOutsideOfEventStream];
+ [_view getIndexFromMouseSpot:
+ [_view convertPoint:self.mouseLocationOutsideOfEventStream
+ fromView:nil]];
if (cursorIndex != _highlightedIndex && cursorIndex != _functionButton) {
[_toolTip hide];
} else if (noDelay) {
[_toolTip.displayTimer fire];
}
- if (cursorIndex >= 0 && cursorIndex < _indexRange.length &&
+ if (cursorIndex >= 0 && cursorIndex < _view.candidateCount &&
_highlightedIndex != cursorIndex) {
[self highlightFunctionButton:kVoidSymbol delayToolTip:!noDelay];
if (theme.linear && _view.truncated[cursorIndex]) {
[_toolTip
- showWithToolTip:
- [_view.textStorage.mutableString
- substringWithRange:NSMakeRange(
- _view.candidateRanges[cursorIndex]
- .location,
- _view.candidateRanges[cursorIndex]
- .length)]
+ showWithToolTip:[_view.contents.mutableString
+ substringWithRange:
+ RangeCandidate(
+ _view.candidateRanges[cursorIndex])]
withDelay:NO];
} else if (noDelay) {
[_toolTip showWithToolTip:NSLocalizedString(@"candidate", nil)
withDelay:!noDelay];
}
self.sectionNum = cursorIndex / theme.pageSize;
- [_inputController performAction:kHIGHLIGHT
- onIndex:cursorIndex + _indexRange.location];
+ [_inputController
+ performAction:kHIGHLIGHT
+ onIndex:(SquirrelIndex)(cursorIndex + _indexRange.location)];
} else if ((cursorIndex == kPageUpKey || cursorIndex == kPageDownKey ||
cursorIndex == kExpandButton ||
cursorIndex == kBackSpaceKey) &&
@@ -4283,49 +4322,106 @@ - (void)sendEvent:(NSEvent*)event {
[_toolTip.displayTimer invalidate];
break;
case NSEventTypeLeftMouseDragged:
- // reset the remember_size references after moving the panel
+ // reset the `remember_size` references after moving the panel
_maxSize = NSZeroSize;
[self performWindowDragWithEvent:event];
break;
case NSEventTypeScrollWheel: {
- CGFloat scrollThreshold =
- theme.candidateParagraphStyle.minimumLineHeight +
- theme.candidateParagraphStyle.lineSpacing;
- static NSPoint scrollLocus = NSZeroPoint;
+ CGFloat scrollThreshold = _view.scrollView.lineScroll;
+ static NSPoint scrollLocus;
+ static BOOL scrollByLine;
if (event.phase == NSEventPhaseBegan) {
scrollLocus = NSZeroPoint;
+ scrollByLine = NO;
} else if ((event.phase == NSEventPhaseNone ||
event.momentumPhase == NSEventPhaseNone) &&
!isnan(scrollLocus.x) && !isnan(scrollLocus.y)) {
+ CGFloat scrollDistance = 0.0;
// determine scrolling direction by confining to sectors within ±30º of
// any axis
if (fabs(event.scrollingDeltaX) >
fabs(event.scrollingDeltaY) * sqrt(3.0)) {
- scrollLocus.x += event.scrollingDeltaX *
- (event.hasPreciseScrollingDeltas ? 1 : 10);
+ scrollDistance =
+ event.scrollingDeltaX *
+ (event.hasPreciseScrollingDeltas ? 1 : scrollThreshold);
+ scrollLocus.x += scrollDistance;
} else if (fabs(event.scrollingDeltaY) >
fabs(event.scrollingDeltaX) * sqrt(3.0)) {
- scrollLocus.y += event.scrollingDeltaY *
- (event.hasPreciseScrollingDeltas ? 1 : 10);
+ scrollDistance =
+ event.scrollingDeltaY *
+ (event.hasPreciseScrollingDeltas ? 1 : scrollThreshold);
+ scrollLocus.y += scrollDistance;
}
// compare accumulated locus length against threshold and limit paging
// to max once
if (scrollLocus.x > scrollThreshold) {
- [_inputController
- performAction:kPROCESS
- onIndex:(theme.vertical ? kPageDownKey : kPageUpKey)];
- scrollLocus = NSMakePoint(NAN, NAN);
+ if (theme.vertical && NSMaxY(_view.scrollView.documentVisibleRect) <
+ NSMaxY(_view.documentRect) - 0.1) {
+ scrollByLine = YES;
+ NSPoint origin = _view.scrollView.contentView.bounds.origin;
+ origin.y += fmin(scrollDistance,
+ NSMaxY(_view.documentRect) -
+ NSMaxY(_view.scrollView.documentVisibleRect));
+ [_view.scrollView.contentView scrollToPoint:origin];
+ _view.scrollView.verticalScroller.doubleValue =
+ NSMinY(_view.scrollView.documentVisibleRect) /
+ _view.clippedHeight;
+ } else if (!scrollByLine) {
+ [_inputController
+ performAction:kPROCESS
+ onIndex:(theme.vertical ? kPageDownKey : kPageUpKey)];
+ scrollLocus = NSMakePoint(NAN, NAN);
+ }
} else if (scrollLocus.y > scrollThreshold) {
- [_inputController performAction:kPROCESS onIndex:kPageUpKey];
- scrollLocus = NSMakePoint(NAN, NAN);
+ if (NSMinY(_view.scrollView.documentVisibleRect) >
+ NSMinY(_view.documentRect) + 0.1) {
+ scrollByLine = YES;
+ NSPoint origin = _view.scrollView.contentView.bounds.origin;
+ origin.y -= fmin(scrollDistance,
+ NSMinY(_view.scrollView.documentVisibleRect) -
+ NSMinY(_view.documentRect));
+ [_view.scrollView.contentView scrollToPoint:origin];
+ _view.scrollView.verticalScroller.doubleValue =
+ NSMinY(_view.scrollView.documentVisibleRect) /
+ _view.clippedHeight;
+ } else if (!scrollByLine) {
+ [_inputController performAction:kPROCESS onIndex:kPageUpKey];
+ scrollLocus = NSMakePoint(NAN, NAN);
+ }
} else if (scrollLocus.x < -scrollThreshold) {
- [_inputController
- performAction:kPROCESS
- onIndex:(theme.vertical ? kPageUpKey : kPageDownKey)];
- scrollLocus = NSMakePoint(NAN, NAN);
+ if (theme.vertical && NSMinY(_view.scrollView.documentVisibleRect) >
+ NSMinY(_view.documentRect) + 0.1) {
+ scrollByLine = YES;
+ NSPoint origin = _view.scrollView.contentView.bounds.origin;
+ origin.y += fmax(scrollDistance,
+ NSMinY(_view.documentRect) -
+ NSMinY(_view.scrollView.documentVisibleRect));
+ [_view.scrollView.contentView scrollToPoint:origin];
+ _view.scrollView.verticalScroller.doubleValue =
+ NSMinY(_view.scrollView.documentVisibleRect) /
+ _view.clippedHeight;
+ } else if (!scrollByLine) {
+ [_inputController
+ performAction:kPROCESS
+ onIndex:(theme.vertical ? kPageUpKey : kPageDownKey)];
+ scrollLocus = NSMakePoint(NAN, NAN);
+ }
} else if (scrollLocus.y < -scrollThreshold) {
- [_inputController performAction:kPROCESS onIndex:kPageDownKey];
- scrollLocus = NSMakePoint(NAN, NAN);
+ if (NSMaxY(_view.scrollView.documentVisibleRect) <
+ NSMaxY(_view.documentRect) - 0.1) {
+ scrollByLine = YES;
+ NSPoint origin = _view.scrollView.contentView.bounds.origin;
+ origin.y -= fmax(scrollDistance,
+ NSMaxY(_view.scrollView.documentVisibleRect) -
+ NSMaxY(_view.documentRect));
+ [_view.scrollView.contentView scrollToPoint:origin];
+ _view.scrollView.verticalScroller.doubleValue =
+ NSMinY(_view.scrollView.documentVisibleRect) /
+ _view.clippedHeight;
+ } else if (!scrollByLine) {
+ [_inputController performAction:kPROCESS onIndex:kPageDownKey];
+ scrollLocus = NSMakePoint(NAN, NAN);
+ }
}
}
} break;
@@ -4347,66 +4443,44 @@ - (void)highlightCandidate:(NSUInteger)highlightedIndex
NSUInteger priorIndex = i + priorSectionNum * theme.pageSize;
if ((_sectionNum != priorSectionNum || priorIndex == priorHilitedIndex) &&
priorIndex < _indexRange.length) {
+ SquirrelCandidateRanges priorRange = _view.candidateRanges[priorIndex];
NSColor* labelColor =
priorIndex == priorHilitedIndex && _sectionNum == priorSectionNum
? theme.labelForeColor
: theme.dimmedLabelForeColor;
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:labelColor
- range:NSMakeRange(_view.candidateRanges[priorIndex].location,
- _view.candidateRanges[priorIndex].text)];
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:labelColor
+ range:RangeLabel(priorRange)];
if (priorIndex == priorHilitedIndex) {
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:theme.textForeColor
- range:NSMakeRange(
- _view.candidateRanges[priorIndex].location +
- _view.candidateRanges[priorIndex].text,
- _view.candidateRanges[priorIndex].comment -
- _view.candidateRanges[priorIndex].text)];
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:theme.commentForeColor
- range:NSMakeRange(
- _view.candidateRanges[priorIndex].location +
- _view.candidateRanges[priorIndex].comment,
- _view.candidateRanges[priorIndex].length -
- _view.candidateRanges[priorIndex].comment)];
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:theme.textForeColor
+ range:RangeText(priorRange)];
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:theme.commentForeColor
+ range:RangeComment(priorRange)];
}
}
NSUInteger newIndex = i + _sectionNum * theme.pageSize;
- if ((_sectionNum != priorSectionNum || newIndex == _highlightedIndex) &&
+ if ((_sectionNum != priorSectionNum || newIndex == highlightedIndex) &&
newIndex < _indexRange.length) {
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:newIndex == _highlightedIndex
- ? theme.hilitedLabelForeColor
- : theme.labelForeColor
- range:NSMakeRange(_view.candidateRanges[newIndex].location,
- _view.candidateRanges[newIndex].text)];
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:newIndex == _highlightedIndex
- ? theme.hilitedTextForeColor
- : theme.textForeColor
- range:NSMakeRange(_view.candidateRanges[newIndex].location +
- _view.candidateRanges[newIndex].text,
- _view.candidateRanges[newIndex].comment -
- _view.candidateRanges[newIndex].text)];
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:newIndex == _highlightedIndex
- ? theme.hilitedCommentForeColor
- : theme.commentForeColor
- range:NSMakeRange(
- _view.candidateRanges[newIndex].location +
- _view.candidateRanges[newIndex].comment,
- _view.candidateRanges[newIndex].length -
- _view.candidateRanges[newIndex].comment)];
+ SquirrelCandidateRanges newRange = _view.candidateRanges[newIndex];
+ NSColor* labelColor = newIndex == highlightedIndex
+ ? theme.hilitedLabelForeColor
+ : theme.labelForeColor;
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:labelColor
+ range:RangeLabel(newRange)];
+ if (newIndex == highlightedIndex) {
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:theme.hilitedTextForeColor
+ range:RangeText(newRange)];
+ [_view.contents addAttribute:NSForegroundColorAttributeName
+ value:theme.hilitedCommentForeColor
+ range:RangeComment(newRange)];
+ }
}
}
- [_view highlightCandidate:_highlightedIndex];
+ [_view highlightCandidate:highlightedIndex];
}
- (void)highlightFunctionButton:(SquirrelIndex)functionButton
@@ -4417,61 +4491,55 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton
SquirrelTheme* theme = _view.currentTheme;
switch (_functionButton) {
case kPageUpKey:
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:theme.preeditForeColor
- range:NSMakeRange(_view.pagingRange.location, 1)];
+ [_view.pagingContents addAttribute:NSForegroundColorAttributeName
+ value:theme.preeditForeColor
+ range:NSMakeRange(0, 1)];
break;
case kPageDownKey:
- [_view.textStorage
+ [_view.pagingContents
addAttribute:NSForegroundColorAttributeName
value:theme.preeditForeColor
- range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)];
+ range:NSMakeRange(_view.pagingContents.length - 1, 1)];
break;
case kExpandButton:
- [_view.textStorage
+ [_view.pagingContents
addAttribute:NSForegroundColorAttributeName
value:theme.preeditForeColor
- range:NSMakeRange(_view.pagingRange.location +
- _view.pagingRange.length / 2,
- 1)];
+ range:NSMakeRange(_view.pagingContents.length / 2, 1)];
break;
case kBackSpaceKey:
- [_view.textStorage
+ [_view.preeditContents
addAttribute:NSForegroundColorAttributeName
value:theme.preeditForeColor
- range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)];
+ range:NSMakeRange(_view.preeditContents.length - 1, 1)];
break;
}
_functionButton = functionButton;
switch (_functionButton) {
case kPageUpKey:
- [_view.textStorage
- addAttribute:NSForegroundColorAttributeName
- value:theme.hilitedPreeditForeColor
- range:NSMakeRange(_view.pagingRange.location, 1)];
+ [_view.pagingContents addAttribute:NSForegroundColorAttributeName
+ value:theme.hilitedPreeditForeColor
+ range:NSMakeRange(0, 1)];
functionButton = _pageNum == 0 ? kHomeKey : kPageUpKey;
[_toolTip showWithToolTip:NSLocalizedString(
_pageNum == 0 ? @"home" : @"page_up", nil)
withDelay:delay];
break;
case kPageDownKey:
- [_view.textStorage
+ [_view.pagingContents
addAttribute:NSForegroundColorAttributeName
value:theme.hilitedPreeditForeColor
- range:NSMakeRange(NSMaxRange(_view.pagingRange) - 1, 1)];
+ range:NSMakeRange(_view.pagingContents.length - 1, 1)];
functionButton = _finalPage ? kEndKey : kPageDownKey;
[_toolTip showWithToolTip:NSLocalizedString(
_finalPage ? @"end" : @"page_down", nil)
withDelay:delay];
break;
case kExpandButton:
- [_view.textStorage
+ [_view.pagingContents
addAttribute:NSForegroundColorAttributeName
value:theme.hilitedPreeditForeColor
- range:NSMakeRange(_view.pagingRange.location +
- _view.pagingRange.length / 2,
- 1)];
+ range:NSMakeRange(_view.pagingContents.length / 2, 1)];
functionButton = _locked ? kLockButton
: _view.expanded ? kCompressButton
: kExpandButton;
@@ -4482,10 +4550,10 @@ - (void)highlightFunctionButton:(SquirrelIndex)functionButton
withDelay:delay];
break;
case kBackSpaceKey:
- [_view.textStorage
+ [_view.preeditContents
addAttribute:NSForegroundColorAttributeName
value:theme.hilitedPreeditForeColor
- range:NSMakeRange(NSMaxRange(_view.preeditRange) - 1, 1)];
+ range:NSMakeRange(_view.preeditContents.length - 1, 1)];
functionButton = _caretPos == NSNotFound || _caretPos == 0
? kEscapeKey
: kBackSpaceKey;
@@ -4516,15 +4584,38 @@ - (void)updateDisplayParameters __attribute__((objc_direct)) {
_initPosition = YES;
_maxSize = NSZeroSize;
- // size limits on textContainer
- NSRect screenRect = _screen.visibleFrame;
SquirrelTheme* theme = _view.currentTheme;
_view.textView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical;
+ _view.preeditView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical;
+ _view.pagingView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical;
+ _view.statusView.layoutOrientation = (NSTextLayoutOrientation)theme.vertical;
// rotate the view, the core in vertical mode!
- self.contentView.boundsRotation = theme.vertical ? -90.0 : 0.0;
+ self.contentView.boundsRotation = theme.vertical ? 90.0 : 0.0;
_view.textView.boundsRotation = 0.0;
+ _view.preeditView.boundsRotation = 0.0;
+ _view.pagingView.boundsRotation = 0.0;
+ _view.statusView.boundsRotation = 0.0;
_view.textView.boundsOrigin = NSZeroPoint;
+ _view.preeditView.boundsOrigin = NSZeroPoint;
+ _view.pagingView.boundsOrigin = NSZeroPoint;
+ _view.statusView.boundsOrigin = NSZeroPoint;
+
+ _view.scrollView.lineScroll = theme.candidateParagraphStyle.minimumLineHeight;
+ if (@available(macOS 12.0, *)) {
+ SquirrelTextLayoutManager* textLayoutManager =
+ (SquirrelTextLayoutManager*)_view.textView.textLayoutManager;
+ textLayoutManager.contentBlock =
+ theme.linear ? kLinearCandidatesBlock : kStackedCandidatesBlock;
+ } else {
+ SquirrelLayoutManager* layoutManager =
+ (SquirrelLayoutManager*)_view.textView.layoutManager;
+ layoutManager.contentBlock =
+ theme.linear ? kLinearCandidatesBlock : kStackedCandidatesBlock;
+ ;
+ }
+ // size limits on textContainer
+ NSRect screenRect = _screen.visibleFrame;
CGFloat textWidthRatio =
fmin(0.8, 1.0 / (theme.vertical ? 4 : 3) +
[theme.textAttrs[NSFontAttributeName] pointSize] / 144.0);
@@ -4541,17 +4632,19 @@ - (void)updateDisplayParameters __attribute__((objc_direct)) {
(theme.fullWidth * 2) -
theme.fullWidth;
}
- CGFloat textHeightLimit =
- ceil((theme.vertical ? NSWidth(screenRect) : NSHeight(screenRect)) * 0.8 -
- theme.borderInsets.height * 2 - theme.linespace);
- _view.textView.textContainer.size =
- NSMakeSize(_textWidthLimit, textHeightLimit);
+ _view.textView.textContainer.size = NSMakeSize(_textWidthLimit, CGFLOAT_MAX);
+ _view.preeditView.textContainer.size =
+ NSMakeSize(_textWidthLimit, CGFLOAT_MAX);
+ _view.pagingView.textContainer.size =
+ NSMakeSize(_textWidthLimit, CGFLOAT_MAX);
+ _view.statusView.textContainer.size =
+ NSMakeSize(_textWidthLimit, CGFLOAT_MAX);
// opacity and transluecency
+ self.alphaValue = theme.opacity;
if (@available(macOS 10.14, *)) {
- _back.hidden = theme.translucency < 0.001;
+ _back.hidden = theme.translucency < 0.001f;
}
- self.alphaValue = theme.opacity;
// resize background image, if any
if (theme.backImage.valid) {
@@ -4576,7 +4669,7 @@ - (void)show __attribute__((objc_direct)) {
}
// Break line if the text is too long, based on screen size.
SquirrelTheme* theme = _view.currentTheme;
- NSEdgeInsets insets = _view.marginInsets;
+ NSSize border = theme.borderInsets;
CGFloat textWidthRatio =
fmin(0.8, 1.0 / (theme.vertical ? 4 : 3) +
[theme.textAttrs[NSFontAttributeName] pointSize] / 144.0);
@@ -4586,7 +4679,6 @@ - (void)show __attribute__((objc_direct)) {
// squirrel panel position
BOOL sweepVertical = NSWidth(_IbeamRect) > NSHeight(_IbeamRect);
NSRect contentRect = _view.contentRect;
- contentRect.size.width -= _view.trailPadding;
// fixed line length (text width), but not applicable to status message
if (theme.lineLength > 0.1 && _statusMessage == nil) {
contentRect.size.width = _textWidthLimit;
@@ -4594,35 +4686,32 @@ - (void)show __attribute__((objc_direct)) {
// remember panel size (fix the top leading anchor of the panel in screen
// coordiantes) but only when the text would expand on the side of upstream
// (i.e. towards the beginning of text)
- if (theme.rememberSize && _statusMessage == nil) {
- if (theme.lineLength < 0.1 &&
- (theme.vertical
- ? (sweepVertical
- ? (NSMinY(_IbeamRect) -
- fmax(NSWidth(contentRect), _maxSize.width) -
- insets.right <
- NSMinY(screenRect))
- : (NSMinY(_IbeamRect) - kOffsetGap -
- NSHeight(screenRect) * textWidthRatio - insets.left -
- insets.right <
- NSMinY(screenRect)))
- : (sweepVertical
- ? (NSMinX(_IbeamRect) - kOffsetGap -
- NSWidth(screenRect) * textWidthRatio - insets.left -
- insets.right >=
- NSMinX(screenRect))
- : (NSMaxX(_IbeamRect) +
- fmax(NSWidth(contentRect), _maxSize.width) +
- insets.right >
- NSMaxX(screenRect))))) {
+ if (theme.rememberSize && _view.statusView.hidden) {
+ if (theme.lineLength < 0.1 && theme.vertical
+ ? sweepVertical ? (NSMinY(_IbeamRect) -
+ fmax(NSWidth(contentRect), _maxSize.width) -
+ border.width - floor(theme.fullWidth * 0.5) <
+ NSMinY(screenRect))
+ : (NSMinY(_IbeamRect) - kOffsetGap -
+ NSHeight(screenRect) * textWidthRatio -
+ border.width * 2 - theme.fullWidth <
+ NSMinY(screenRect))
+ : sweepVertical
+ ? (NSMinX(_IbeamRect) - kOffsetGap -
+ NSWidth(screenRect) * textWidthRatio - border.width * 2 -
+ theme.fullWidth >=
+ NSMinX(screenRect))
+ : (NSMaxX(_IbeamRect) + fmax(NSWidth(contentRect), _maxSize.width) +
+ border.width + floor(theme.fullWidth * 0.5) >
+ NSMaxX(screenRect))) {
if (NSWidth(contentRect) >= _maxSize.width) {
_maxSize.width = NSWidth(contentRect);
} else {
contentRect.size.width = _maxSize.width;
}
}
- CGFloat textHeight = fmax(NSHeight(contentRect), _maxSize.height) +
- insets.top + insets.bottom;
+ CGFloat textHeight =
+ fmax(NSHeight(contentRect), _maxSize.height) + border.height * 2;
if (theme.vertical ? (NSMinX(_IbeamRect) - textHeight -
(sweepVertical ? kOffsetGap : 0) <
NSMinX(screenRect))
@@ -4642,14 +4731,13 @@ - (void)show __attribute__((objc_direct)) {
// following system UI, middle-align status message with cursor
_initPosition = YES;
if (theme.vertical) {
- windowRect.size.width =
- NSHeight(contentRect) + insets.top + insets.bottom;
+ windowRect.size.width = NSHeight(contentRect) + border.height * 2;
windowRect.size.height =
- NSWidth(contentRect) + insets.left + insets.right;
+ NSWidth(contentRect) + border.width * 2 + theme.fullWidth;
} else {
- windowRect.size.width = NSWidth(contentRect) + insets.left + insets.right;
- windowRect.size.height =
- NSHeight(contentRect) + insets.top + insets.bottom;
+ windowRect.size.width =
+ NSWidth(contentRect) + border.width * 2 + theme.fullWidth;
+ windowRect.size.height = NSHeight(contentRect) + border.height * 2;
}
if (sweepVertical) {
// vertically centre-align (MidY) in screen coordinates
@@ -4665,21 +4753,21 @@ - (void)show __attribute__((objc_direct)) {
} else {
if (theme.vertical) {
// anchor is the top right corner in screen coordinates (MaxX, MaxY)
- windowRect =
- NSMakeRect(NSMaxX(self.frame) - NSHeight(contentRect) - insets.top -
- insets.bottom,
- NSMaxY(self.frame) - NSWidth(contentRect) - insets.left -
- insets.right,
- NSHeight(contentRect) + insets.top + insets.bottom,
- NSWidth(contentRect) + insets.left + insets.right);
- _initPosition |= NSIntersectsRect(windowRect, _IbeamRect);
+ windowRect = NSMakeRect(
+ NSMaxX(self.frame) - NSHeight(contentRect) - border.height * 2,
+ NSMaxY(self.frame) - NSWidth(contentRect) - border.width * 2 -
+ theme.fullWidth,
+ NSHeight(contentRect) + border.height * 2,
+ NSWidth(contentRect) + border.width * 2 + theme.fullWidth);
+ _initPosition |= NSIntersectsRect(windowRect, _IbeamRect) ||
+ !NSContainsRect(screenRect, windowRect);
if (_initPosition) {
if (!sweepVertical) {
// To avoid jumping up and down while typing, use the lower screen
// when typing on upper, and vice versa
if (NSMinY(_IbeamRect) - kOffsetGap -
- NSHeight(screenRect) * textWidthRatio - insets.left -
- insets.right <
+ NSHeight(screenRect) * textWidthRatio - border.width * 2 -
+ theme.fullWidth <
NSMinY(screenRect)) {
windowRect.origin.y = NSMaxY(_IbeamRect) + kOffsetGap;
} else {
@@ -4688,7 +4776,7 @@ - (void)show __attribute__((objc_direct)) {
}
// Make the right edge of candidate block fixed at the left of cursor
windowRect.origin.x =
- NSMinX(_IbeamRect) + insets.top - NSWidth(windowRect);
+ NSMinX(_IbeamRect) + border.height - NSWidth(windowRect);
} else {
if (NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect) <
NSMinX(screenRect)) {
@@ -4697,26 +4785,27 @@ - (void)show __attribute__((objc_direct)) {
windowRect.origin.x =
NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect);
}
- windowRect.origin.y =
- NSMinY(_IbeamRect) + insets.left - NSHeight(windowRect);
+ windowRect.origin.y = NSMinY(_IbeamRect) + border.width +
+ ceil(theme.fullWidth * 0.5) -
+ NSHeight(windowRect);
}
}
} else {
// anchor is the top left corner in screen coordinates (MinX, MaxY)
- windowRect =
- NSMakeRect(NSMinX(self.frame),
- NSMaxY(self.frame) - NSHeight(contentRect) - insets.top -
- insets.bottom,
- NSWidth(contentRect) + insets.left + insets.right,
- NSHeight(contentRect) + insets.top + insets.bottom);
- _initPosition |= NSIntersectsRect(windowRect, _IbeamRect);
+ windowRect = NSMakeRect(
+ NSMinX(self.frame),
+ NSMaxY(self.frame) - NSHeight(contentRect) - border.height * 2,
+ NSWidth(contentRect) + border.width * 2 + theme.fullWidth,
+ NSHeight(contentRect) + border.height * 2);
+ _initPosition |= NSIntersectsRect(windowRect, _IbeamRect) ||
+ !NSContainsRect(screenRect, windowRect);
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(_IbeamRect) - kOffsetGap -
- NSWidth(screenRect) * textWidthRatio - insets.left -
- insets.right >=
+ NSWidth(screenRect) * textWidthRatio - border.width * 2 -
+ theme.fullWidth >=
NSMinX(screenRect)) {
windowRect.origin.x =
NSMinX(_IbeamRect) - kOffsetGap - NSWidth(windowRect);
@@ -4724,7 +4813,7 @@ - (void)show __attribute__((objc_direct)) {
windowRect.origin.x = NSMaxX(_IbeamRect) + kOffsetGap;
}
windowRect.origin.y =
- NSMinY(_IbeamRect) + insets.top - NSHeight(windowRect);
+ NSMinY(_IbeamRect) + border.height - NSHeight(windowRect);
} else {
if (NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect) <
NSMinY(screenRect)) {
@@ -4733,19 +4822,19 @@ - (void)show __attribute__((objc_direct)) {
windowRect.origin.y =
NSMinY(_IbeamRect) - kOffsetGap - NSHeight(windowRect);
}
- windowRect.origin.x = NSMaxX(_IbeamRect) - insets.left;
+ windowRect.origin.x =
+ NSMaxX(_IbeamRect) - border.width - ceil(theme.fullWidth * 0.5);
}
}
}
}
- if (_view.preeditRange.length > 0) {
+ if (!_view.preeditView.hidden) {
if (_initPosition) {
_anchorOffset = 0.0;
}
if (theme.vertical != sweepVertical) {
- CGFloat anchorOffset =
- NSHeight([_view blockRectForRange:_view.preeditRange]);
+ CGFloat anchorOffset = NSHeight(_view.preeditRect);
if (theme.vertical) {
windowRect.origin.x += anchorOffset - _anchorOffset;
} else {
@@ -4800,19 +4889,59 @@ - (void)show __attribute__((objc_direct)) {
theme.vertical ? NSMakePoint(0.0, NSWidth(windowRect)) : NSZeroPoint;
NSRect viewRect = self.contentView.bounds;
_view.frame = viewRect;
- _view.textView.frame = NSMakeRect(
- NSMinX(viewRect) + insets.left - _view.textView.textContainerOrigin.x,
- NSMinY(viewRect) + insets.bottom - _view.textView.textContainerOrigin.y,
- NSWidth(viewRect) - insets.left - insets.right,
- NSHeight(viewRect) - insets.top - insets.bottom);
- if (@available(macOS 10.14, *)) {
- if (!_back.hidden) {
- _back.frame = viewRect;
- }
+ if (!_view.statusView.hidden) {
+ _view.statusView.frame = NSMakeRect(
+ NSMinX(viewRect) + border.width + ceil(theme.fullWidth * 0.5) -
+ _view.statusView.textContainerOrigin.x,
+ NSMinY(viewRect) + border.height -
+ _view.statusView.textContainerOrigin.y,
+ NSWidth(viewRect) - border.width * 2 - theme.fullWidth,
+ NSHeight(viewRect) - border.height * 2);
+ }
+ if (!_view.preeditView.hidden) {
+ _view.preeditView.frame = NSMakeRect(
+ NSMinX(viewRect) + border.width + ceil(theme.fullWidth * 0.5) -
+ _view.preeditView.textContainerOrigin.x,
+ NSMinY(viewRect) + border.height -
+ _view.preeditView.textContainerOrigin.y,
+ NSWidth(viewRect) - border.width * 2 - theme.fullWidth,
+ NSHeight(_view.preeditRect));
+ }
+ if (!_view.pagingView.hidden) {
+ CGFloat leadOrigin =
+ theme.linear
+ ? NSMaxX(viewRect) - NSWidth(_view.pagingRect) - border.width +
+ ceil(theme.fullWidth * 0.5)
+ : NSMinX(viewRect) + border.width + ceil(theme.fullWidth * 0.5);
+ _view.pagingView.frame = NSMakeRect(
+ leadOrigin - _view.pagingView.textContainerOrigin.x,
+ NSMaxY(viewRect) - border.height - NSHeight(_view.pagingRect) -
+ _view.pagingView.textContainerOrigin.y,
+ (theme.linear ? NSWidth(_view.pagingRect)
+ : NSWidth(viewRect) - border.width * 2) -
+ theme.fullWidth,
+ NSHeight(_view.pagingRect));
+ }
+ if (!_view.scrollView.hidden) {
+ _view.scrollView.frame = NSMakeRect(
+ NSMinX(viewRect) + border.width,
+ NSMinY(viewRect) + NSMinY(_view.candidatesRect),
+ NSWidth(viewRect) - border.width * 2, NSHeight(_view.candidatesRect));
+ _view.documentView.frame =
+ NSMakeRect(0.0, 0.0, NSWidth(viewRect) - border.width * 2,
+ NSHeight(_view.documentRect));
+ _view.textView.frame = NSMakeRect(
+ ceil(theme.fullWidth * 0.5) - _view.textView.textContainerOrigin.x,
+ ceil(theme.linespace * 0.5) - _view.textView.textContainerOrigin.y,
+ NSWidth(viewRect) - border.width * 2 - theme.fullWidth,
+ NSHeight(_view.documentRect) - theme.linespace);
+ }
+ if (!_back.hidden) {
+ _back.frame = viewRect;
}
[self orderFront:nil];
// reset to initial position after showing status message
- _initPosition = _statusMessage != nil;
+ _initPosition = !_view.statusView.hidden;
_needsRedraw = NO;
// voila !
}
@@ -4830,8 +4959,20 @@ - (void)hide __attribute__((objc_direct)) {
self.sectionNum = 0;
}
+static CGFloat textWidth(NSAttributedString* string, BOOL vertical) {
+ if (vertical) {
+ NSMutableAttributedString* verticalString = string.mutableCopy;
+ [verticalString addAttribute:NSVerticalGlyphFormAttributeName
+ value:@YES
+ range:NSMakeRange(0, verticalString.length)];
+ return ceil(verticalString.size.width);
+ } else {
+ return ceil(string.size.width);
+ }
+}
+
// Main function to add attributes to text output from librime
-- (void)showPreedit:(NSString*)preeditString
+- (void)showPreedit:(NSString*)preedit
selRange:(NSRange)selRange
caretPos:(NSUInteger)caretPos
candidateIndices:(NSRange)indexRange
@@ -4844,14 +4985,18 @@ - (void)showPreedit:(NSString*)preeditString
_pageNum = pageNum;
_finalPage = finalPage;
_functionButton = kVoidSymbol;
- if (indexRange.length > 0 || preeditString.length > 0) {
+ if (indexRange.length > 0 || preedit.length > 0) {
_statusMessage = nil;
+ if (_view.statusContents.length > 0) {
+ [_view.statusContents
+ deleteCharactersInRange:NSMakeRange(0, _view.statusContents.length)];
+ }
if (_statusTimer.valid) {
[_statusTimer invalidate];
_statusTimer = nil;
}
} else {
- if (_statusMessage) {
+ if (_statusMessage != nil) {
[self showStatus:_statusMessage];
_statusMessage = nil;
} else if (!_statusTimer.valid) {
@@ -4861,89 +5006,84 @@ - (void)showPreedit:(NSString*)preeditString
}
SquirrelTheme* theme = _view.currentTheme;
- NSTextStorage* contents = _view.textStorage;
NSParagraphStyle* rulerAttrsPreedit;
- NSSize priorSize = contents.length > 0 ? _view.contentRect.size : NSZeroSize;
- if ((indexRange.length == 0 && preeditString &&
- _view.preeditRange.length > 0) ||
- !updateCandidates) {
- rulerAttrsPreedit = [contents attribute:NSParagraphStyleAttributeName
- atIndex:0
- effectiveRange:NULL];
+ NSSize priorSize = _view.candidateCount > 0 || !_view.preeditView.hidden
+ ? _view.contentRect.size
+ : NSZeroSize;
+ if ((indexRange.length == 0 || !updateCandidates) && preedit.length > 0 &&
+ !_view.preeditView.hidden) {
+ rulerAttrsPreedit =
+ [_view.preeditContents attribute:NSParagraphStyleAttributeName
+ atIndex:0
+ effectiveRange:NULL];
}
SquirrelCandidateRanges* candidateRanges;
BOOL* truncated;
if (updateCandidates) {
- contents.attributedString = NSAttributedString.alloc.init;
+ [_view.contents
+ deleteCharactersInRange:NSMakeRange(0, _view.contents.length)];
if (theme.lineLength > 0.1) {
_maxSize.width = fmin(theme.lineLength, _textWidthLimit);
}
_indexRange = indexRange;
_highlightedIndex = highlightedIndex;
- candidateRanges = indexRange.length > 0
- ? new SquirrelCandidateRanges[indexRange.length]
- : NULL;
- truncated = indexRange.length > 0 ? new BOOL[indexRange.length] : NULL;
+ candidateRanges = new SquirrelCandidateRanges[indexRange.length];
+ truncated = new BOOL[indexRange.length];
}
- NSRange preeditRange = NSMakeRange(NSNotFound, 0);
- NSRange pagingRange = NSMakeRange(NSNotFound, 0);
- NSUInteger candidatesStart = 0;
- NSUInteger pagingStart = 0;
// preedit
- if (preeditString) {
- NSMutableAttributedString* preedit =
- [NSMutableAttributedString.alloc initWithString:preeditString
- attributes:theme.preeditAttrs];
- [preedit.mutableString
+ if (preedit.length > 0) {
+ _view.preeditContents.attributedString =
+ [NSAttributedString.alloc initWithString:preedit
+ attributes:theme.preeditAttrs];
+ [_view.preeditContents.mutableString
appendString:rulerAttrsPreedit ? @"\t" : kFullWidthSpace];
if (selRange.length > 0) {
- [preedit addAttribute:NSForegroundColorAttributeName
- value:theme.hilitedPreeditForeColor
- range:selRange];
+ [_view.preeditContents addAttribute:NSForegroundColorAttributeName
+ value:theme.hilitedPreeditForeColor
+ range:selRange];
NSNumber* padding =
@(ceil(theme.preeditParagraphStyle.minimumLineHeight * 0.05));
if (selRange.location > 0) {
- [preedit addAttribute:NSKernAttributeName
- value:padding
- range:NSMakeRange(selRange.location - 1, 1)];
+ [_view.preeditContents
+ addAttribute:NSKernAttributeName
+ value:padding
+ range:NSMakeRange(selRange.location - 1, 1)];
}
- if (NSMaxRange(selRange) < preedit.length) {
- [preedit addAttribute:NSKernAttributeName
- value:padding
- range:NSMakeRange(NSMaxRange(selRange) - 1, 1)];
+ if (NSMaxRange(selRange) < _view.preeditContents.length) {
+ [_view.preeditContents
+ addAttribute:NSKernAttributeName
+ value:padding
+ range:NSMakeRange(NSMaxRange(selRange) - 1, 1)];
}
}
- [preedit appendAttributedString:caretPos == NSNotFound || caretPos == 0
- ? theme.symbolDeleteStroke
- : theme.symbolDeleteFill];
+ [_view.preeditContents
+ appendAttributedString:caretPos == NSNotFound || caretPos == 0
+ ? theme.symbolDeleteStroke
+ : theme.symbolDeleteFill];
// force caret to be rendered sideways, instead of uprights, in vertical
// orientation
if (theme.vertical && caretPos != NSNotFound) {
- [preedit addAttribute:NSVerticalGlyphFormAttributeName
- value:@(NO)
- range:NSMakeRange(caretPos, 1)];
+ [_view.preeditContents addAttribute:NSVerticalGlyphFormAttributeName
+ value:@NO
+ range:NSMakeRange(caretPos, 1)];
}
- preeditRange = NSMakeRange(0, preedit.length);
- if (rulerAttrsPreedit) {
- [preedit addAttribute:NSParagraphStyleAttributeName
- value:rulerAttrsPreedit
- range:preeditRange];
+ if (rulerAttrsPreedit != nil) {
+ [_view.preeditContents
+ addAttribute:NSParagraphStyleAttributeName
+ value:rulerAttrsPreedit
+ range:NSMakeRange(0, _view.preeditContents.length)];
}
- if (updateCandidates) {
- [contents appendAttributedString:preedit];
- if (indexRange.length > 0) {
- [contents.mutableString appendString:@"\n"];
- } else {
- self.sectionNum = 0;
- goto AdjustAlignment;
- }
+ if (updateCandidates && indexRange.length == 0) {
+ self.sectionNum = 0;
+ goto AdjustAlignment;
} else {
- [contents replaceCharactersInRange:_view.preeditRange
- withAttributedString:preedit];
- [_view setPreeditRange:preeditRange hilitedPreeditRange:selRange];
+ [_view setHilitedPreeditRange:selRange];
}
+ } else if (_view.preeditContents.length > 0) {
+ [_view.preeditContents
+ deleteCharactersInRange:NSMakeRange(0, _view.preeditContents.length)];
}
if (!updateCandidates) {
@@ -4957,7 +5097,6 @@ - (void)showPreedit:(NSString*)preeditString
}
// candidate items
- candidatesStart = contents.length;
for (NSUInteger idx = 0; idx < indexRange.length; ++idx) {
NSUInteger col = idx % theme.pageSize;
NSMutableAttributedString* candidate =
@@ -5002,7 +5141,7 @@ - (void)showPreedit:(NSString*)preeditString
NSUInteger start = candidateRanges[0].location;
for (NSUInteger i = 1; i <= idx; ++i) {
if (i == idx || truncated[i] != isTruncated) {
- [contents
+ [_view.contents
addAttribute:NSParagraphStyleAttributeName
value:isTruncated ? theme.truncatedParagraphStyle
: theme.candidateParagraphStyle
@@ -5016,46 +5155,45 @@ - (void)showPreedit:(NSString*)preeditString
}
}
} else {
- [contents
- addAttribute:NSParagraphStyleAttributeName
- value:theme.candidateParagraphStyle
- range:NSMakeRange(candidatesStart,
- contents.length - candidatesStart)];
+ [_view.contents addAttribute:NSParagraphStyleAttributeName
+ value:theme.candidateParagraphStyle
+ range:NSMakeRange(0, _view.contents.length)];
}
}
}
// store final in-candidate locations of label, text, and comment
textRange = [candidate.mutableString rangeOfString:text];
- if (idx > 0 && (!theme.linear || !truncated[idx - 1])) {
- // separator: linear = "\u3000\x1D"; tabular = "\u3000\t\x1D"; stacked =
- // "\n"
- [contents appendAttributedString:theme.separator];
- if (theme.linear && col == 0) {
- [contents.mutableString appendString:@"\n"];
- }
+ if (idx > 0 && col == 0 && theme.linear && !truncated[idx - 1]) {
+ [_view.contents.mutableString appendString:@"\n"];
}
- NSUInteger candidateStart = contents.length;
+ NSUInteger candidateStart = _view.contents.length;
SquirrelCandidateRanges ranges = {.location = candidateStart,
.text = textRange.location,
.comment = NSMaxRange(textRange)};
- [contents appendAttributedString:candidate];
+ [_view.contents appendAttributedString:candidate];
// for linear layout, middle-truncate candidates that are longer than one
// line
if (theme.linear &&
- ceil(candidate.size.width) >
- _textWidthLimit - theme.fullWidth * (theme.tabular ? 2 : 1) - 0.1) {
+ textWidth(candidate, theme.vertical) >
+ _textWidthLimit - theme.fullWidth * (theme.tabular ? 3 : 2)) {
truncated[idx] = YES;
- ranges.length = contents.length - candidateStart;
+ ranges.length = _view.contents.length - candidateStart;
candidateRanges[idx] = ranges;
if (idx < indexRange.length - 1 || theme.tabular || theme.showPaging) {
- [contents.mutableString appendString:@"\n"];
+ [_view.contents.mutableString appendString:@"\n"];
}
- [contents addAttribute:NSParagraphStyleAttributeName
- value:theme.truncatedParagraphStyle
- range:NSMakeRange(candidateStart,
- contents.length - candidateStart)];
+ [_view.contents
+ addAttribute:NSParagraphStyleAttributeName
+ value:theme.truncatedParagraphStyle
+ range:NSMakeRange(candidateStart,
+ _view.contents.length - candidateStart)];
} else {
+ if (theme.linear || idx < indexRange.length - 1) {
+ // separator: linear = "\u3000\x1D"; tabular = "\u3000\t\x1D"; stacked =
+ // "\n"
+ [_view.contents appendAttributedString:theme.separator];
+ }
truncated[idx] = NO;
ranges.length = candidate.length + (theme.tabular ? 3
: theme.linear ? 2
@@ -5066,128 +5204,91 @@ - (void)showPreedit:(NSString*)preeditString
// paging indication
if (theme.tabular || theme.showPaging) {
- NSMutableAttributedString* paging;
if (theme.tabular) {
- paging = [NSMutableAttributedString.alloc
- initWithAttributedString:_locked ? theme.symbolLock
- : _view.expanded ? theme.symbolCompress
- : theme.symbolExpand];
+ _view.pagingContents.attributedString = _locked ? theme.symbolLock
+ : _view.expanded
+ ? theme.symbolCompress
+ : theme.symbolExpand;
} else {
NSAttributedString* pageNumString = [NSAttributedString.alloc
initWithString:[NSString stringWithFormat:@"%lu", pageNum + 1]
attributes:theme.pagingAttrs];
- if (theme.vertical) {
- paging = [NSMutableAttributedString.alloc
- initWithAttributedString:
- [pageNumString attributedStringHorizontalInVerticalForms]];
- } else {
- paging = [NSMutableAttributedString.alloc
- initWithAttributedString:pageNumString];
- }
+ _view.pagingContents.attributedString =
+ theme.vertical
+ ? pageNumString.attributedStringHorizontalInVerticalForms
+ : pageNumString;
}
if (theme.showPaging) {
- [paging insertAttributedString:_pageNum > 0 ? theme.symbolBackFill
- : theme.symbolBackStroke
- atIndex:0];
- [paging.mutableString insertString:kFullWidthSpace atIndex:1];
- [paging.mutableString appendString:kFullWidthSpace];
- [paging appendAttributedString:_finalPage ? theme.symbolForwardStroke
- : theme.symbolForwardFill];
- }
- if (!theme.linear || !truncated[indexRange.length - 1]) {
- [contents appendAttributedString:theme.separator];
- if (theme.linear) {
- [contents replaceCharactersInRange:NSMakeRange(contents.length, 0)
- withString:@"\n"];
- }
- }
- pagingStart = contents.length;
- if (theme.linear) {
- [contents appendAttributedString:[NSAttributedString.alloc
- initWithString:kFullWidthSpace
- attributes:theme.pagingAttrs]];
+ [_view.pagingContents
+ insertAttributedString:_pageNum > 0 ? theme.symbolBackFill
+ : theme.symbolBackStroke
+ atIndex:0];
+ [_view.pagingContents.mutableString insertString:kFullWidthSpace
+ atIndex:1];
+ [_view.pagingContents.mutableString appendString:kFullWidthSpace];
+ [_view.pagingContents
+ appendAttributedString:_finalPage ? theme.symbolForwardStroke
+ : theme.symbolForwardFill];
}
- [contents appendAttributedString:paging];
- pagingRange = NSMakeRange(contents.length - paging.length, paging.length);
- } else if (theme.linear && !truncated[indexRange.length - 1]) {
- [contents appendAttributedString:theme.separator];
+ } else if (_view.pagingContents.length > 0) {
+ [_view.pagingContents
+ deleteCharactersInRange:NSMakeRange(0, _view.pagingContents.length)];
}
AdjustAlignment:
- [_view estimateBoundsForPreedit:preeditRange
- candidates:candidateRanges
- truncation:truncated
- count:indexRange.length
- paging:pagingRange];
+ [_view estimateBoundsOnScreen:_screen.visibleFrame
+ withPreedit:preedit.length > 0
+ candidates:candidateRanges
+ truncation:truncated
+ count:indexRange.length
+ paging:indexRange.length > 0 &&
+ (theme.tabular || theme.showPaging)];
CGFloat textWidth =
- fmin(fmax(NSMaxX(_view.contentRect) - _view.trailPadding, _maxSize.width),
- _textWidthLimit);
+ clamp(NSWidth(_view.contentRect), _maxSize.width, _textWidthLimit);
// right-align the backward delete symbol
- if (preeditRange.length > 0 &&
- NSMaxX([_view blockRectForRange:NSMakeRange(preeditRange.length - 1,
- 1)]) < textWidth - 0.1) {
- [contents replaceCharactersInRange:NSMakeRange(preeditRange.length - 2, 1)
- withString:@"\t"];
+ if (preedit.length > 0 && rulerAttrsPreedit == nil) {
+ [_view.preeditContents
+ replaceCharactersInRange:NSMakeRange(_view.preeditContents.length - 2,
+ 1)
+ withString:@"\t"];
NSMutableParagraphStyle* rulerAttrs =
theme.preeditParagraphStyle.mutableCopy;
rulerAttrs.tabStops =
@[ [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentRight
location:textWidth
options:@{}] ];
- [contents addAttribute:NSParagraphStyleAttributeName
- value:rulerAttrs
- range:preeditRange];
+ [_view.preeditContents
+ addAttribute:NSParagraphStyleAttributeName
+ value:rulerAttrs
+ range:NSMakeRange(0, _view.preeditContents.length)];
}
- if (pagingRange.length > 0 &&
- NSMaxX([_view blockRectForRange:pagingRange]) < textWidth - 0.1) {
+ if (!theme.linear && theme.showPaging) {
NSMutableParagraphStyle* rulerAttrsPaging =
theme.pagingParagraphStyle.mutableCopy;
- if (theme.linear) {
- [contents replaceCharactersInRange:NSMakeRange(pagingStart, 1)
- withString:@"\t"];
- rulerAttrsPaging.tabStops =
- @[ [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentRight
- location:textWidth
- options:@{}] ];
- } else {
- [contents replaceCharactersInRange:NSMakeRange(pagingStart + 1, 1)
- withString:@"\t"];
- [contents replaceCharactersInRange:NSMakeRange(contents.length - 2, 1)
- withString:@"\t"];
- rulerAttrsPaging.tabStops = @[
- [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentCenter
- location:textWidth * 0.5
- options:@{}],
- [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentRight
- location:textWidth
- options:@{}]
- ];
- }
- [contents
+ [_view.pagingContents replaceCharactersInRange:NSMakeRange(1, 1)
+ withString:@"\t"];
+ [_view.pagingContents
+ replaceCharactersInRange:NSMakeRange(_view.pagingContents.length - 2, 1)
+ withString:@"\t"];
+ rulerAttrsPaging.tabStops = @[
+ [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentCenter
+ location:textWidth * 0.5
+ options:@{}],
+ [NSTextTab.alloc initWithTextAlignment:NSTextAlignmentRight
+ location:textWidth
+ options:@{}]
+ ];
+ [_view.pagingContents
addAttribute:NSParagraphStyleAttributeName
value:rulerAttrsPaging
- range:NSMakeRange(pagingStart, contents.length - pagingStart)];
- }
-
- // text done!
- CGFloat topMargin =
- preeditString || theme.linear ? 0.0 : ceil(theme.linespace * 0.5);
- CGFloat bottomMargin =
- !theme.linear && indexRange.length > 0 && pagingRange.length == 0
- ? floor(theme.linespace * 0.5)
- : 0.0;
- NSEdgeInsets insets =
- NSEdgeInsetsMake(theme.borderInsets.height + topMargin,
- theme.borderInsets.width + ceil(theme.fullWidth * 0.5),
- theme.borderInsets.height + bottomMargin,
- theme.borderInsets.width + floor(theme.fullWidth * 0.5));
+ range:NSMakeRange(0, _view.pagingContents.length)];
+ }
self.animationBehavior = caretPos == NSNotFound
? NSWindowAnimationBehaviorUtilityWindow
: NSWindowAnimationBehaviorDefault;
- [_view drawViewWithInsets:insets
- hilitedIndex:highlightedIndex
- hilitedPreeditRange:selRange];
+ [_view drawViewWithHilitedIndex:highlightedIndex
+ hilitedPreeditRange:selRange];
NSSize newSize = _view.contentRect.size;
_needsRedraw |= !NSEqualSizes(priorSize, newSize);
[self show];
@@ -5216,24 +5317,25 @@ - (void)updateStatusLong:(NSString*)messageLong
}
- (void)showStatus:(NSString*)message __attribute__((objc_direct)) {
- SquirrelTheme* theme = _view.currentTheme;
- NSTextStorage* contents = _view.textStorage;
- NSSize priorSize = contents.length > 0 ? _view.contentRect.size : NSZeroSize;
+ NSSize priorSize =
+ !_view.statusView.hidden ? _view.contentRect.size : NSZeroSize;
+
+ [_view.contents
+ deleteCharactersInRange:NSMakeRange(0, _view.contents.length)];
+ [_view.preeditContents
+ deleteCharactersInRange:NSMakeRange(0, _view.preeditContents.length)];
+ [_view.pagingContents
+ deleteCharactersInRange:NSMakeRange(0, _view.pagingContents.length)];
- contents.attributedString = [NSAttributedString.alloc
+ _view.statusContents.attributedString = [NSAttributedString.alloc
initWithString:[NSString stringWithFormat:@"\u3000\u2002%@", message]
- attributes:theme.statusAttrs];
-
- [_view estimateBoundsForPreedit:NSMakeRange(NSNotFound, 0)
- candidates:NULL
- truncation:NULL
- count:0
- paging:NSMakeRange(NSNotFound, 0)];
- NSEdgeInsets insets =
- NSEdgeInsetsMake(theme.borderInsets.height,
- theme.borderInsets.width + ceil(theme.fullWidth * 0.5),
- theme.borderInsets.height,
- theme.borderInsets.width + floor(theme.fullWidth * 0.5));
+ attributes:_view.currentTheme.statusAttrs];
+ [_view estimateBoundsOnScreen:_screen.visibleFrame
+ withPreedit:NO
+ candidates:NULL
+ truncation:NULL
+ count:0
+ paging:NO];
// disable remember_size and fixed line_length for status messages
_initPosition = YES;
@@ -5242,9 +5344,8 @@ - (void)showStatus:(NSString*)message __attribute__((objc_direct)) {
[_statusTimer invalidate];
}
self.animationBehavior = NSWindowAnimationBehaviorUtilityWindow;
- [_view drawViewWithInsets:insets
- hilitedIndex:NSNotFound
- hilitedPreeditRange:NSMakeRange(NSNotFound, 0)];
+ [_view drawViewWithHilitedIndex:NSNotFound
+ hilitedPreeditRange:NSMakeRange(NSNotFound, 0)];
NSSize newSize = _view.contentRect.size;
_needsRedraw |= !NSEqualSizes(priorSize, newSize);
[self show];
@@ -5281,13 +5382,13 @@ - (void)loadConfig:(SquirrelConfig*)config {
updateWithConfig:config
styleOptions:_optionSwitcher.optionStates
scriptVariant:_optionSwitcher.currentScriptVariant
- forAppearance:defaultAppear];
+ forAppearance:kDefaultAppearance];
if (@available(macOS 10.14, *)) {
[SquirrelView.darkTheme
updateWithConfig:config
styleOptions:_optionSwitcher.optionStates
scriptVariant:_optionSwitcher.currentScriptVariant
- forAppearance:darkAppear];
+ forAppearance:kDarkAppearance];
}
[self getLocked];
[self updateDisplayParameters];
diff --git a/input_source.mm b/input_source.mm
index 03269d57e..1fa3ca425 100644
--- a/input_source.mm
+++ b/input_source.mm
@@ -19,20 +19,19 @@ typedef CF_OPTIONS(CFIndex, RimeInputMode) {
RimeInputMode GetEnabledInputModes(void);
void RegisterInputSource(void) {
- if (GetEnabledInputModes()) { // Already registered
+ if (GetEnabledInputModes() != 0) { // Already registered
return;
}
CFURLRef installPathURL = CFURLCreateFromFileSystemRepresentation(
NULL, (UInt8*)kInstallPath, (CFIndex)strlen(kInstallPath), false);
- if (installPathURL) {
+ if (installPathURL != NULL) {
TISRegisterInputSource((CFURLRef)CFAutorelease(installPathURL));
NSLog(@"Registered input source from %s", kInstallPath);
}
}
void EnableInputSource(void) {
- RimeInputMode input_modes_enabled = GetEnabledInputModes();
- if (input_modes_enabled != 0) {
+ if (GetEnabledInputModes() != 0) {
// keep user's manually enabled input modes
return;
}
@@ -44,11 +43,16 @@ void EnableInputSource(void) {
CFBundleCopyLocalizationsForPreferences(localizations, NULL);
if (CFArrayGetCount(preferred) > 0) {
CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(preferred, 0);
- if (!CFStringCompare(language, CFSTR("zh-Hans"), 0)) {
+ if (CFStringCompare(language, CFSTR("zh-Hans"),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
input_modes_to_enable |= HANS_INPUT_MODE;
- } else if (!CFStringCompare(language, CFSTR("zh-Hant"), 0)) {
+ } else if (CFStringCompare(language, CFSTR("zh-Hant"),
+ kCFCompareCaseInsensitive) ==
+ kCFCompareEqualTo) {
input_modes_to_enable |= HANT_INPUT_MODE;
- } else if (!CFStringCompare(language, CFSTR("zh-HK"), 0)) {
+ } else if (CFStringCompare(language, CFSTR("zh-HK"),
+ kCFCompareCaseInsensitive) ==
+ kCFCompareEqualTo) {
input_modes_to_enable |= CANT_INPUT_MODE;
}
} else {
@@ -66,17 +70,16 @@ void EnableInputSource(void) {
CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceID);
// NSLog(@"Examining input source: %@", sourceID);
- if ((!CFStringCompare(sourceID, kHansInputModeID, 0) &&
+ if ((CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo &&
(input_modes_to_enable & HANS_INPUT_MODE)) ||
- (!CFStringCompare(sourceID, kHantInputModeID, 0) &&
+ (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo &&
(input_modes_to_enable & HANT_INPUT_MODE)) ||
- (!CFStringCompare(sourceID, kCantInputModeID, 0) &&
+ (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo &&
(input_modes_to_enable & CANT_INPUT_MODE))) {
CFBooleanRef isEnabled = (CFBooleanRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceIsEnabled);
if (!CFBooleanGetValue(isEnabled)) {
- OSStatus enableError = TISEnableInputSource(inputSource);
- if (enableError) {
+ if (OSStatus enableError = TISEnableInputSource(inputSource) != 0) {
NSLog(@"Failed to enable input source: %@ (%@)", sourceID,
[NSError errorWithDomain:NSOSStatusErrorDomain
code:enableError
@@ -100,18 +103,21 @@ void SelectInputSource(void) {
CFBundleCopyLocalizationsForPreferences(localizations, NULL);
for (CFIndex i = 0; i < CFArrayGetCount(preferred); ++i) {
CFStringRef language = (CFStringRef)CFArrayGetValueAtIndex(preferred, i);
- if (!CFStringCompare(language, CFSTR("zh-Hans"), 0) &&
+ if (CFStringCompare(language, CFSTR("zh-Hans"),
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo &&
(enabled_input_modes & HANS_INPUT_MODE)) {
input_mode_to_select = HANS_INPUT_MODE;
break;
- }
- if (!CFStringCompare(language, CFSTR("zh-Hant"), 0) &&
- (enabled_input_modes & HANT_INPUT_MODE)) {
+ } else if (CFStringCompare(language, CFSTR("zh-Hant"),
+ kCFCompareCaseInsensitive) ==
+ kCFCompareEqualTo &&
+ (enabled_input_modes & HANT_INPUT_MODE)) {
input_mode_to_select = HANT_INPUT_MODE;
break;
- }
- if (!CFStringCompare(language, CFSTR("zh-HK"), 0) &&
- (enabled_input_modes & CANT_INPUT_MODE)) {
+ } else if (CFStringCompare(language, CFSTR("zh-HK"),
+ kCFCompareCaseInsensitive) ==
+ kCFCompareEqualTo &&
+ (enabled_input_modes & CANT_INPUT_MODE)) {
input_mode_to_select = CANT_INPUT_MODE;
break;
}
@@ -132,11 +138,11 @@ void SelectInputSource(void) {
CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceID);
// NSLog(@"Examining input source: %@", sourceID);
- if ((!CFStringCompare(sourceID, kHansInputModeID, 0) &&
+ if ((CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo &&
((input_mode_to_select & HANS_INPUT_MODE) != 0)) ||
- (!CFStringCompare(sourceID, kHantInputModeID, 0) &&
+ (CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo &&
((input_mode_to_select & HANT_INPUT_MODE) != 0)) ||
- (!CFStringCompare(sourceID, kCantInputModeID, 0) &&
+ (CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo &&
((input_mode_to_select & CANT_INPUT_MODE) != 0))) {
// select the first enabled input mode in Squirrel.
CFBooleanRef isSelectable = (CFBooleanRef)TISGetInputSourceProperty(
@@ -144,8 +150,7 @@ void SelectInputSource(void) {
CFBooleanRef isSelected = (CFBooleanRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceIsSelected);
if (!CFBooleanGetValue(isSelected) && CFBooleanGetValue(isSelectable)) {
- OSStatus selectError = TISSelectInputSource(inputSource);
- if (selectError) {
+ if (OSStatus selectError = TISSelectInputSource(inputSource) != 0) {
NSLog(@"Failed to select input source: %@ (%@)", sourceID,
[NSError errorWithDomain:NSOSStatusErrorDomain
code:selectError
@@ -172,11 +177,10 @@ void DisableInputSource(void) {
CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceID);
// NSLog(@"Examining input source: %@", sourceID);
- if (!CFStringCompare(sourceID, kHansInputModeID, 0) ||
- !CFStringCompare(sourceID, kHantInputModeID, 0) ||
- !CFStringCompare(sourceID, kCantInputModeID, 0)) {
- OSStatus disableError = TISDisableInputSource(inputSource);
- if (disableError) {
+ if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo ||
+ CFStringCompare(sourceID, kHantInputModeID, 0) == kCFCompareEqualTo ||
+ CFStringCompare(sourceID, kCantInputModeID, 0) == kCFCompareEqualTo) {
+ if (OSStatus disableError = TISDisableInputSource(inputSource) != 0) {
NSLog(@"Failed to disable input source: %@ (%@)", sourceID,
[NSError errorWithDomain:NSOSStatusErrorDomain
code:disableError
@@ -202,16 +206,14 @@ RimeInputMode GetEnabledInputModes(void) {
CFStringRef sourceID = (CFStringRef)TISGetInputSourceProperty(
inputSource, kTISPropertyInputSourceID);
// NSLog(@"Examining input source: %@", sourceID);
- if (!CFStringCompare(sourceID, kHansInputModeID, 0) ||
- !CFStringCompare(sourceID, kHantInputModeID, 0) ||
- !CFStringCompare(sourceID, kCantInputModeID, 0)) {
- if (!CFStringCompare(sourceID, kHansInputModeID, 0)) {
- input_modes |= HANS_INPUT_MODE;
- } else if (!CFStringCompare(sourceID, kHantInputModeID, 0)) {
- input_modes |= HANT_INPUT_MODE;
- } else if (!CFStringCompare(sourceID, kCantInputModeID, 0)) {
- input_modes |= CANT_INPUT_MODE;
- }
+ if (CFStringCompare(sourceID, kHansInputModeID, 0) == kCFCompareEqualTo) {
+ input_modes |= HANS_INPUT_MODE;
+ } else if (CFStringCompare(sourceID, kHantInputModeID, 0) ==
+ kCFCompareEqualTo) {
+ input_modes |= HANT_INPUT_MODE;
+ } else if (CFStringCompare(sourceID, kCantInputModeID, 0) ==
+ kCFCompareEqualTo) {
+ input_modes |= CANT_INPUT_MODE;
}
}
CFRelease(sourceList);
diff --git a/macos_keycode.mm b/macos_keycode.mm
index 5ac8bf926..78dcbfd9f 100644
--- a/macos_keycode.mm
+++ b/macos_keycode.mm
@@ -173,6 +173,7 @@ int rime_keycode_from_mac_keycode(ushort mac_keycode) {
return XK_Eisu_toggle;
case kVK_JIS_Kana:
return XK_Kana_Shift;
+
default:
return 0;
}
@@ -278,11 +279,12 @@ int rime_keycode_from_keychar(unichar keychar, bool shift, bool caps) {
};
int rime_modifiers_from_name(const char* modifier_name) {
- if (!modifier_name)
+ if (modifier_name == NULL) {
return 0;
+ }
for (int i = 0; i < 6; ++i) {
- if (!strcmp(modifier_name, rime_modidifers[i])) {
- return (1 << (i < 4 ? i : i + 22));
+ if (strcmp(modifier_name, rime_modidifers[i]) == 0) {
+ return 1 << (i < 4 ? i : i + 22);
}
}
return 0;
diff --git a/main.mm b/main.mm
index 35e6224e0..462d7fa13 100644
--- a/main.mm
+++ b/main.mm
@@ -14,7 +14,7 @@
static NSString* const kConnectionName = @"Squirrel_1_Connection";
int main(int argc, char* argv[]) {
- if (argc > 1 && !strcmp("--quit", argv[1])) {
+ if (argc > 1 && strcmp("--quit", argv[1]) == 0) {
NSString* bundleId = NSBundle.mainBundle.bundleIdentifier;
NSArray* runningSquirrels =
[NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId];
@@ -24,35 +24,35 @@ int main(int argc, char* argv[]) {
return 0;
}
- if (argc > 1 && !strcmp("--reload", argv[1])) {
+ if (argc > 1 && strcmp("--reload", argv[1]) == 0) {
[NSDistributedNotificationCenter.defaultCenter
postNotificationName:@"SquirrelReloadNotification"
object:nil];
return 0;
}
- if (argc > 1 && (!strcmp("--register-input-source", argv[1]) ||
- !strcmp("--install", argv[1]))) {
+ if (argc > 1 && (strcmp("--register-input-source", argv[1]) == 0 ||
+ strcmp("--install", argv[1]) == 0)) {
RegisterInputSource();
return 0;
}
- if (argc > 1 && !strcmp("--enable-input-source", argv[1])) {
+ if (argc > 1 && strcmp("--enable-input-source", argv[1]) == 0) {
EnableInputSource();
return 0;
}
- if (argc > 1 && !strcmp("--disable-input-source", argv[1])) {
+ if (argc > 1 && strcmp("--disable-input-source", argv[1]) == 0) {
DisableInputSource();
return 0;
}
- if (argc > 1 && !strcmp("--select-input-source", argv[1])) {
+ if (argc > 1 && strcmp("--select-input-source", argv[1]) == 0) {
SelectInputSource();
return 0;
}
- if (argc > 1 && !strcmp("--build", argv[1])) {
+ if (argc > 1 && strcmp("--build", argv[1]) == 0) {
// notification
show_notification("deploy_update");
// build all schemas in current directory
@@ -63,7 +63,7 @@ int main(int argc, char* argv[]) {
return rime_get_api()->deploy() ? 0 : 1;
}
- if (argc > 1 && !strcmp("--sync", argv[1])) {
+ if (argc > 1 && strcmp("--sync", argv[1]) == 0) {
[NSDistributedNotificationCenter.defaultCenter
postNotificationName:@"SquirrelSyncNotification"
object:nil];