Skip to content

Commit

Permalink
expandable tabular
Browse files Browse the repository at this point in the history
  • Loading branch information
groverlynn committed Feb 26, 2024
1 parent afe0217 commit aea97ec
Show file tree
Hide file tree
Showing 14 changed files with 1,261 additions and 449 deletions.
15 changes: 15 additions & 0 deletions Assets.xcassets/Symbols/chevron.down.symbolset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"symbol-rendering-intent" : "template"
},
"symbols" : [
{
"filename" : "chevron.down.svg",
"idiom" : "universal"
}
]
}
160 changes: 160 additions & 0 deletions Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions Assets.xcassets/Symbols/chevron.up.symbolset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"symbol-rendering-intent" : "template"
},
"symbols" : [
{
"filename" : "chevron.up.svg",
"idiom" : "universal"
}
]
}
160 changes: 160 additions & 0 deletions Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Assets.xcassets/Symbols/lock.fill.symbolset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"symbols" : [
{
"filename" : "lock.fill.svg",
"idiom" : "universal"
}
]
}
160 changes: 160 additions & 0 deletions Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 23 additions & 14 deletions SquirrelInputController.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@

@interface SquirrelInputController : IMKInputController

// kPROCESS accepts miscellaneous / function keys (e.g. XK_Escape)
// The remaining 3 actions accept candidate indices (int), starting from item 0 on page 0
typedef NS_ENUM(NSInteger, SquirrelAction) {
kSELECT = 1, // accepts indices in digits, selection keys, and keycodes (XK_Escape)
kHILITE = 2, // accepts indices in digits and selection keys (char '1' / 'A')
kDELETE = 3 // only accepts indices in digits (int 1)
kPROCESS = 0,
kSELECT = 1,
kHIGHLIGHT = 2,
kDELETE = 3
};

typedef NS_ENUM(NSUInteger, SquirrelIndex) {
// 0 ... 9 are ordinal digits, used as (int) index
// 0x21 ... 0x7e are ASCII chars (as selection keys)
// other rime keycodes (as function keys), for paging etc.
kBackSpace = 0xff08, // XK_BackSpace
kEscape = 0xff1b, // XK_Escape
kCodeInput = 0xff37, // XK_Codeinput
kHome = 0xff50, // XK_Home
kPageUp = 0xff55, // XK_Page_Up
kPageDown = 0xff56, // XK_Page_Down
kEnd = 0xff57, // XK_End
kVoidSymbol = 0xffffff // XK_VoidSymbol
// 0, 1, 2 ... are ordinal digits, used as (int) indices
// 0xFFXX are rime keycodes (as function keys), for paging etc.
kBackSpaceKey = 0xff08, // XK_BackSpace
kEscapeKey = 0xff1b, // XK_Escape
kCodeInputArea = 0xff37, // XK_Codeinput
kHomeKey = 0xff50, // XK_Home
kLeftKey = 0xff51, // XK_Left
kUpKey = 0xff52, // XK_Up
kRightKey = 0xff53, // XK_Right
kDownKey = 0xff54, // XK_Down
kPageUpKey = 0xff55, // XK_Page_Up
kPageDownKey = 0xff56, // XK_Page_Down
kEndKey = 0xff57, // XK_End
kExpandButton = 0xff04,
kCompressButton = 0xff05,
kLockButton = 0xff06,
kVoidSymbol = 0xffffff // XK_VoidSymbol
};

@property(class, weak, readonly) SquirrelInputController *currentController;
Expand Down
136 changes: 101 additions & 35 deletions SquirrelInputController.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#import <IOKit/hid/IOHIDLib.h>
#import <IOKit/hidsystem/IOHIDLib.h>

const int N_KEY_ROLL_OVER = 50;
static const int N_KEY_ROLL_OVER = 50;
static NSString *const kFullWidthSpace = @" ";

@implementation SquirrelInputController {
Expand All @@ -22,6 +22,7 @@ @implementation SquirrelInputController {
NSArray<NSString *> *_candidates;
NSEventModifierFlags _lastModifiers;
uint _lastEventCount;
NSUInteger _lastPageNum;
RimeSessionId _session;
NSString *_schemaId;
BOOL _inlinePreedit;
Expand All @@ -41,7 +42,8 @@ @implementation SquirrelInputController {
}

static SquirrelInputController *_currentController = nil;
static NSMapTable<SquirrelInputController *, NSDate *> *_controllerDeactivationTime = NSMapTable.weakToWeakObjectsMapTable;
static NSMapTable<SquirrelInputController *, NSDate *> *_controllerDeactivationTime =
NSMapTable.weakToWeakObjectsMapTable;

+ (void)setCurrentController:(SquirrelInputController *)controller {
_currentController = controller;
Expand Down Expand Up @@ -212,9 +214,9 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index
if (_inlineCandidate && !_inlinePreedit) {
return NO;
}
[self perform:kSELECT onIndex:kEnd];
[self perform:kPROCESS onIndex:kEndKey];
} else if (point.x < head.x || index <= 0) {
[self perform:kSELECT onIndex:kHome];
[self perform:kPROCESS onIndex:kHomeKey];
} else {
[self moveCursor:_caretPos
toPosition:index
Expand All @@ -240,17 +242,30 @@ void set_CapsLock_LED_state(bool target_state) {

- (BOOL)processKey:(int)rime_keycode
modifiers:(int)rime_modifiers {
SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
// with linear candidate list, arrow keys may behave differently.
Bool is_linear = (Bool)NSApp.squirrelAppDelegate.panel.linear;
Bool is_linear = (Bool)panel.linear;
if (is_linear != rime_get_api()->get_option(_session, "_linear")) {
rime_get_api()->set_option(_session, "_linear", is_linear);
}
// with vertical text, arrow keys may behave differently.
Bool is_vertical = (Bool)NSApp.squirrelAppDelegate.panel.vertical;
Bool is_vertical = (Bool)panel.vertical;
if (is_vertical != rime_get_api()->get_option(_session, "_vertical")) {
rime_get_api()->set_option(_session, "_vertical", is_vertical);
}

if (panel.tabular && !rime_modifiers &&
(is_vertical ? rime_keycode == XK_Left || rime_keycode == XK_Right
: rime_keycode == XK_Up || rime_keycode == XK_Down)) {
NSUInteger newIndex = [panel candidateIndexOnDirection:(SquirrelIndex)rime_keycode];
if (newIndex != NSNotFound) {
if (!panel.locked && !panel.expanded && rime_keycode == (is_vertical ? XK_Left : XK_Down)) {
panel.expanded = YES;
}
return rime_get_api()->highlight_candidate(_session, newIndex);
}
}

BOOL handled = (BOOL)rime_get_api()->process_key(_session, rime_keycode, rime_modifiers);
//NSLog(@"rime_keycode: 0x%x, rime_modifiers: 0x%x, handled = %d", rime_keycode, rime_modifiers, handled);

Expand Down Expand Up @@ -337,24 +352,26 @@ - (void)perform:(SquirrelAction)action
onIndex:(SquirrelIndex)index {
//NSLog(@"perform action: %lu on index: %lu", action, index);
bool handled = false;
if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) {
handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0);
} else if (index >= 0xff08 && index <= 0xffff && action == kSELECT) {
handled = rime_get_api()->process_key(_session, (int)index, 0);
} else if (index >= 0 && index < 10) {
switch (action) {
case kDELETE:
handled = rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index);
break;
case kSELECT:
handled = rime_get_api()->select_candidate_on_current_page(_session, (size_t)index);
break;
case kHILITE:
handled = rime_get_api()->highlight_candidate_on_current_page(_session, (size_t)index);
break;
}
switch (action) {
case kPROCESS:
if (index >= 0xff08 && index <= 0xffff) {
handled = rime_get_api()->process_key(_session, (int)index, 0);
} else if (index >= kExpandButton && index <= kLockButton) {
handled = true;
}
break;
case kSELECT:
handled = rime_get_api()->select_candidate(_session, index);
break;
case kHIGHLIGHT:
handled = rime_get_api()->highlight_candidate(_session, index);
break;
case kDELETE:
handled = rime_get_api()->delete_candidate(_session, index);
break;
}
if (handled) {
_lastPageNum = NSNotFound;
[self rimeUpdate];
}
}
Expand Down Expand Up @@ -733,6 +750,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit
lastPage:(BOOL)lastPage {
//NSLog(@"showPanelWithPreedit:...:");
_candidates = candidates;
_lastPageNum = pageNum;
SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
panel.IbeamRect = [self getIbeamRect];
if (NSIsEmptyRect(panel.IbeamRect) && panel.statusMessage.length > 0) {
Expand Down Expand Up @@ -819,6 +837,7 @@ - (void)rimeUpdate {
//NSLog(@"rimeUpdate");
BOOL didCommit = [self rimeConsumeCommittedText];

SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
RIME_STRUCT(RimeStatus, status);
if (rime_get_api()->get_status(_session, &status)) {
// enable schema specific ui style
Expand All @@ -830,11 +849,9 @@ - (void)rimeUpdate {
[NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId
withRimeSession:_session];
// inline preedit
_inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit &&
!rime_get_api()->get_option(_session, "no_inline")) ||
_inlinePreedit = (panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) ||
rime_get_api()->get_option(_session, "inline");
_inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate &&
!rime_get_api()->get_option(_session, "no_inline"));
_inlineCandidate = panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline");
// if not inline, embed soft cursor in preedit string
rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit);
} else {
Expand All @@ -846,9 +863,10 @@ - (void)rimeUpdate {

RIME_STRUCT(RimeContext, ctx);
if (rime_get_api()->get_context(_session, &ctx)) {
BOOL showingStatus = NSApp.squirrelAppDelegate.panel.statusMessage.length > 0;
BOOL showingStatus = panel.statusMessage.length > 0;
// update raw input
const char *raw_input = rime_get_api()->get_input(_session);
BOOL didCompose = ![_originalString isEqualToString:raw_input ? @(raw_input) : @""];
_originalString = raw_input ? @(raw_input) : @"";

// update preedit text
Expand All @@ -873,7 +891,31 @@ - (void)rimeUpdate {
NSUInteger end = UTF8LengthToUTF16Length(preedit, ctx.composition.sel_end);
NSUInteger caretPos = UTF8LengthToUTF16Length(preedit, ctx.composition.cursor_pos);
NSUInteger length = UTF8LengthToUTF16Length(preedit, ctx.composition.length);
NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates;
NSUInteger numCandidates = (NSUInteger)ctx.menu.num_candidates;
NSUInteger pageNum = (NSUInteger)ctx.menu.page_no;
NSUInteger pageSize = (NSUInteger)ctx.menu.page_size;
NSUInteger highlightedIndex = numCandidates == 0 ? NSNotFound :
(NSUInteger)ctx.menu.highlighted_candidate_index;
BOOL isLastPage = (BOOL)ctx.menu.is_last_page;

// update discloser and active line status in gridded layout
if (panel.tabular && !showingStatus && numCandidates > 0) {
if (didCompose) {
panel.activePage = 0;
} else if (_lastPageNum != NSNotFound) {
if (!panel.locked && panel.expanded && (pageNum | _lastPageNum | highlightedIndex) == 0) {
panel.expanded = NO;
} else if (!panel.locked && !panel.expanded && pageNum > 0 && pageNum > _lastPageNum) {
panel.expanded = YES;
}
if (panel.expanded && pageNum > _lastPageNum && panel.activePage < 4) {
panel.activePage = MIN(panel.activePage + pageNum - _lastPageNum, isLastPage ? 4UL : 3UL);
} else if (panel.expanded && pageNum < _lastPageNum && panel.activePage > 0) {
panel.activePage = MAX(panel.activePage + pageNum - _lastPageNum, pageNum == 0 ? 0UL : 1UL);
}
}
highlightedIndex += pageSize * panel.activePage;
}

if (showingStatus) {
[self clearBuffer];
Expand Down Expand Up @@ -909,7 +951,7 @@ - (void)rimeUpdate {
}
} else {
if (_inlinePreedit && !_showingSwitcherMenu) {
if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) {
if (_inlinePlaceholder && preeditText.length == 0 && numCandidates > 0) {
[self showPlaceholder:kFullWidthSpace];
} else if (!didCommit || preeditText.length > 0) {
[self showPreeditString:preeditText
Expand All @@ -925,20 +967,44 @@ - (void)rimeUpdate {
}
}
// update candidates
NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate];
NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate];
for (NSUInteger i = 0; i < numCandidate; ++i) {
NSMutableArray *candidates = numCandidates ? [[NSMutableArray alloc] init] : nil;
NSMutableArray *comments = numCandidates ? [[NSMutableArray alloc] init] : nil;
if (numCandidates > 0 && panel.expanded && panel.activePage > 0) {
NSUInteger index = pageSize * (pageNum - panel.activePage);
RimeCandidateListIterator iterator;
if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) {
NSUInteger endIndex = pageSize * pageNum;
while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) {
[candidates addObject:@(iterator.candidate.text)];
[comments addObject:@(iterator.candidate.comment ? : "")];
}
rime_get_api()->candidate_list_end(&iterator);
}
}
for (NSUInteger i = 0; i < numCandidates; ++i) {
[candidates addObject:@(ctx.menu.candidates[i].text)];
[comments addObject:@(ctx.menu.candidates[i].comment ? : "")];
}
if (numCandidates > 0 && panel.expanded && panel.activePage < 5) {
NSUInteger index = pageSize * (pageNum + 1);
RimeCandidateListIterator iterator;
if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) {
NSUInteger endIndex = pageSize * (pageNum + 5 - panel.activePage);
while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) {
[candidates addObject:@(iterator.candidate.text)];
[comments addObject:@(iterator.candidate.comment ? : "")];
}
rime_get_api()->candidate_list_end(&iterator);
}
}
[self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText
selRange:NSMakeRange(start, end - start)
caretPos:_showingSwitcherMenu ? NSNotFound : caretPos
candidates:candidates
comments:comments
highlightedIndex:(NSUInteger)ctx.menu.highlighted_candidate_index
pageNum:(NSUInteger)ctx.menu.page_no
lastPage:(BOOL)ctx.menu.is_last_page];
highlightedIndex:highlightedIndex
pageNum:pageNum
lastPage:isLastPage];
rime_get_api()->free_context(&ctx);
} else {
[self hidePalettes];
Expand Down
9 changes: 7 additions & 2 deletions SquirrelPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) {

// Linear candidate list layout, as opposed to stacked candidate list layout.
@property(nonatomic, readonly) BOOL linear;
// Tabled candidate list layout, a subtype of linear candidate list with tabled layout.
@property(nonatomic, readonly) BOOL tabled;
// Tabular candidate list layout, initializes as tab-aligned linear layout, expandable to stack more candidates
@property(nonatomic, readonly) BOOL tabular;
@property(nonatomic, readonly) BOOL locked;
@property(nonatomic, assign) BOOL expanded;
@property(nonatomic, assign) NSUInteger activePage;
// Vertical text orientation, as opposed to horizontal text orientation.
@property(nonatomic, readonly) BOOL vertical;
// Show preedit text inline.
Expand All @@ -29,6 +32,8 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) {
// position of the text input I-beam cursor on screen.
@property(nonatomic, assign) NSRect IbeamRect;

- (NSUInteger)candidateIndexOnDirection:(SquirrelIndex)arrowKey;

- (void)showPreedit:(NSString *)preedit
selRange:(NSRange)selRange
caretPos:(NSUInteger)caretPos
Expand Down
Loading

0 comments on commit aea97ec

Please sign in to comment.