Skip to content

Commit aea97ec

Browse files
committed
expandable tabular
1 parent afe0217 commit aea97ec

File tree

14 files changed

+1261
-449
lines changed

14 files changed

+1261
-449
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
},
6+
"properties" : {
7+
"symbol-rendering-intent" : "template"
8+
},
9+
"symbols" : [
10+
{
11+
"filename" : "chevron.down.svg",
12+
"idiom" : "universal"
13+
}
14+
]
15+
}

Assets.xcassets/Symbols/chevron.down.symbolset/chevron.down.svg

+160
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
},
6+
"properties" : {
7+
"symbol-rendering-intent" : "template"
8+
},
9+
"symbols" : [
10+
{
11+
"filename" : "chevron.up.svg",
12+
"idiom" : "universal"
13+
}
14+
]
15+
}

Assets.xcassets/Symbols/chevron.up.symbolset/chevron.up.svg

+160
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
},
6+
"symbols" : [
7+
{
8+
"filename" : "lock.fill.svg",
9+
"idiom" : "universal"
10+
}
11+
]
12+
}

Assets.xcassets/Symbols/lock.fill.symbolset/lock.fill.svg

+160
Loading

SquirrelInputController.h

+23-14
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,33 @@
33

44
@interface SquirrelInputController : IMKInputController
55

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

1215
typedef NS_ENUM(NSUInteger, SquirrelIndex) {
13-
// 0 ... 9 are ordinal digits, used as (int) index
14-
// 0x21 ... 0x7e are ASCII chars (as selection keys)
15-
// other rime keycodes (as function keys), for paging etc.
16-
kBackSpace = 0xff08, // XK_BackSpace
17-
kEscape = 0xff1b, // XK_Escape
18-
kCodeInput = 0xff37, // XK_Codeinput
19-
kHome = 0xff50, // XK_Home
20-
kPageUp = 0xff55, // XK_Page_Up
21-
kPageDown = 0xff56, // XK_Page_Down
22-
kEnd = 0xff57, // XK_End
23-
kVoidSymbol = 0xffffff // XK_VoidSymbol
16+
// 0, 1, 2 ... are ordinal digits, used as (int) indices
17+
// 0xFFXX are rime keycodes (as function keys), for paging etc.
18+
kBackSpaceKey = 0xff08, // XK_BackSpace
19+
kEscapeKey = 0xff1b, // XK_Escape
20+
kCodeInputArea = 0xff37, // XK_Codeinput
21+
kHomeKey = 0xff50, // XK_Home
22+
kLeftKey = 0xff51, // XK_Left
23+
kUpKey = 0xff52, // XK_Up
24+
kRightKey = 0xff53, // XK_Right
25+
kDownKey = 0xff54, // XK_Down
26+
kPageUpKey = 0xff55, // XK_Page_Up
27+
kPageDownKey = 0xff56, // XK_Page_Down
28+
kEndKey = 0xff57, // XK_End
29+
kExpandButton = 0xff04,
30+
kCompressButton = 0xff05,
31+
kLockButton = 0xff06,
32+
kVoidSymbol = 0xffffff // XK_VoidSymbol
2433
};
2534

2635
@property(class, weak, readonly) SquirrelInputController *currentController;

SquirrelInputController.m

+101-35
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#import <IOKit/hid/IOHIDLib.h>
1111
#import <IOKit/hidsystem/IOHIDLib.h>
1212

13-
const int N_KEY_ROLL_OVER = 50;
13+
static const int N_KEY_ROLL_OVER = 50;
1414
static NSString *const kFullWidthSpace = @" ";
1515

1616
@implementation SquirrelInputController {
@@ -22,6 +22,7 @@ @implementation SquirrelInputController {
2222
NSArray<NSString *> *_candidates;
2323
NSEventModifierFlags _lastModifiers;
2424
uint _lastEventCount;
25+
NSUInteger _lastPageNum;
2526
RimeSessionId _session;
2627
NSString *_schemaId;
2728
BOOL _inlinePreedit;
@@ -41,7 +42,8 @@ @implementation SquirrelInputController {
4142
}
4243

4344
static SquirrelInputController *_currentController = nil;
44-
static NSMapTable<SquirrelInputController *, NSDate *> *_controllerDeactivationTime = NSMapTable.weakToWeakObjectsMapTable;
45+
static NSMapTable<SquirrelInputController *, NSDate *> *_controllerDeactivationTime =
46+
NSMapTable.weakToWeakObjectsMapTable;
4547

4648
+ (void)setCurrentController:(SquirrelInputController *)controller {
4749
_currentController = controller;
@@ -212,9 +214,9 @@ - (BOOL)mouseDownOnCharacterIndex:(NSUInteger)index
212214
if (_inlineCandidate && !_inlinePreedit) {
213215
return NO;
214216
}
215-
[self perform:kSELECT onIndex:kEnd];
217+
[self perform:kPROCESS onIndex:kEndKey];
216218
} else if (point.x < head.x || index <= 0) {
217-
[self perform:kSELECT onIndex:kHome];
219+
[self perform:kPROCESS onIndex:kHomeKey];
218220
} else {
219221
[self moveCursor:_caretPos
220222
toPosition:index
@@ -240,17 +242,30 @@ void set_CapsLock_LED_state(bool target_state) {
240242

241243
- (BOOL)processKey:(int)rime_keycode
242244
modifiers:(int)rime_modifiers {
245+
SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
243246
// with linear candidate list, arrow keys may behave differently.
244-
Bool is_linear = (Bool)NSApp.squirrelAppDelegate.panel.linear;
247+
Bool is_linear = (Bool)panel.linear;
245248
if (is_linear != rime_get_api()->get_option(_session, "_linear")) {
246249
rime_get_api()->set_option(_session, "_linear", is_linear);
247250
}
248251
// with vertical text, arrow keys may behave differently.
249-
Bool is_vertical = (Bool)NSApp.squirrelAppDelegate.panel.vertical;
252+
Bool is_vertical = (Bool)panel.vertical;
250253
if (is_vertical != rime_get_api()->get_option(_session, "_vertical")) {
251254
rime_get_api()->set_option(_session, "_vertical", is_vertical);
252255
}
253256

257+
if (panel.tabular && !rime_modifiers &&
258+
(is_vertical ? rime_keycode == XK_Left || rime_keycode == XK_Right
259+
: rime_keycode == XK_Up || rime_keycode == XK_Down)) {
260+
NSUInteger newIndex = [panel candidateIndexOnDirection:(SquirrelIndex)rime_keycode];
261+
if (newIndex != NSNotFound) {
262+
if (!panel.locked && !panel.expanded && rime_keycode == (is_vertical ? XK_Left : XK_Down)) {
263+
panel.expanded = YES;
264+
}
265+
return rime_get_api()->highlight_candidate(_session, newIndex);
266+
}
267+
}
268+
254269
BOOL handled = (BOOL)rime_get_api()->process_key(_session, rime_keycode, rime_modifiers);
255270
//NSLog(@"rime_keycode: 0x%x, rime_modifiers: 0x%x, handled = %d", rime_keycode, rime_modifiers, handled);
256271

@@ -337,24 +352,26 @@ - (void)perform:(SquirrelAction)action
337352
onIndex:(SquirrelIndex)index {
338353
//NSLog(@"perform action: %lu on index: %lu", action, index);
339354
bool handled = false;
340-
if (index >= '!' && index <= '~' && (action == kSELECT || action == kHILITE)) {
341-
handled = rime_get_api()->process_key(_session, (int)index, action == kHILITE ? kAltMask : 0);
342-
} else if (index >= 0xff08 && index <= 0xffff && action == kSELECT) {
343-
handled = rime_get_api()->process_key(_session, (int)index, 0);
344-
} else if (index >= 0 && index < 10) {
345-
switch (action) {
346-
case kDELETE:
347-
handled = rime_get_api()->delete_candidate_on_current_page(_session, (size_t)index);
348-
break;
349-
case kSELECT:
350-
handled = rime_get_api()->select_candidate_on_current_page(_session, (size_t)index);
351-
break;
352-
case kHILITE:
353-
handled = rime_get_api()->highlight_candidate_on_current_page(_session, (size_t)index);
354-
break;
355-
}
355+
switch (action) {
356+
case kPROCESS:
357+
if (index >= 0xff08 && index <= 0xffff) {
358+
handled = rime_get_api()->process_key(_session, (int)index, 0);
359+
} else if (index >= kExpandButton && index <= kLockButton) {
360+
handled = true;
361+
}
362+
break;
363+
case kSELECT:
364+
handled = rime_get_api()->select_candidate(_session, index);
365+
break;
366+
case kHIGHLIGHT:
367+
handled = rime_get_api()->highlight_candidate(_session, index);
368+
break;
369+
case kDELETE:
370+
handled = rime_get_api()->delete_candidate(_session, index);
371+
break;
356372
}
357373
if (handled) {
374+
_lastPageNum = NSNotFound;
358375
[self rimeUpdate];
359376
}
360377
}
@@ -733,6 +750,7 @@ - (void)showPanelWithPreedit:(NSString *)preedit
733750
lastPage:(BOOL)lastPage {
734751
//NSLog(@"showPanelWithPreedit:...:");
735752
_candidates = candidates;
753+
_lastPageNum = pageNum;
736754
SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
737755
panel.IbeamRect = [self getIbeamRect];
738756
if (NSIsEmptyRect(panel.IbeamRect) && panel.statusMessage.length > 0) {
@@ -819,6 +837,7 @@ - (void)rimeUpdate {
819837
//NSLog(@"rimeUpdate");
820838
BOOL didCommit = [self rimeConsumeCommittedText];
821839

840+
SquirrelPanel *panel = NSApp.squirrelAppDelegate.panel;
822841
RIME_STRUCT(RimeStatus, status);
823842
if (rime_get_api()->get_status(_session, &status)) {
824843
// enable schema specific ui style
@@ -830,11 +849,9 @@ - (void)rimeUpdate {
830849
[NSApp.squirrelAppDelegate loadSchemaSpecificSettings:_schemaId
831850
withRimeSession:_session];
832851
// inline preedit
833-
_inlinePreedit = (NSApp.squirrelAppDelegate.panel.inlinePreedit &&
834-
!rime_get_api()->get_option(_session, "no_inline")) ||
852+
_inlinePreedit = (panel.inlinePreedit && !rime_get_api()->get_option(_session, "no_inline")) ||
835853
rime_get_api()->get_option(_session, "inline");
836-
_inlineCandidate = (NSApp.squirrelAppDelegate.panel.inlineCandidate &&
837-
!rime_get_api()->get_option(_session, "no_inline"));
854+
_inlineCandidate = panel.inlineCandidate && !rime_get_api()->get_option(_session, "no_inline");
838855
// if not inline, embed soft cursor in preedit string
839856
rime_get_api()->set_option(_session, "soft_cursor", !_inlinePreedit);
840857
} else {
@@ -846,9 +863,10 @@ - (void)rimeUpdate {
846863

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

854872
// update preedit text
@@ -873,7 +891,31 @@ - (void)rimeUpdate {
873891
NSUInteger end = UTF8LengthToUTF16Length(preedit, ctx.composition.sel_end);
874892
NSUInteger caretPos = UTF8LengthToUTF16Length(preedit, ctx.composition.cursor_pos);
875893
NSUInteger length = UTF8LengthToUTF16Length(preedit, ctx.composition.length);
876-
NSUInteger numCandidate = (NSUInteger)ctx.menu.num_candidates;
894+
NSUInteger numCandidates = (NSUInteger)ctx.menu.num_candidates;
895+
NSUInteger pageNum = (NSUInteger)ctx.menu.page_no;
896+
NSUInteger pageSize = (NSUInteger)ctx.menu.page_size;
897+
NSUInteger highlightedIndex = numCandidates == 0 ? NSNotFound :
898+
(NSUInteger)ctx.menu.highlighted_candidate_index;
899+
BOOL isLastPage = (BOOL)ctx.menu.is_last_page;
900+
901+
// update discloser and active line status in gridded layout
902+
if (panel.tabular && !showingStatus && numCandidates > 0) {
903+
if (didCompose) {
904+
panel.activePage = 0;
905+
} else if (_lastPageNum != NSNotFound) {
906+
if (!panel.locked && panel.expanded && (pageNum | _lastPageNum | highlightedIndex) == 0) {
907+
panel.expanded = NO;
908+
} else if (!panel.locked && !panel.expanded && pageNum > 0 && pageNum > _lastPageNum) {
909+
panel.expanded = YES;
910+
}
911+
if (panel.expanded && pageNum > _lastPageNum && panel.activePage < 4) {
912+
panel.activePage = MIN(panel.activePage + pageNum - _lastPageNum, isLastPage ? 4UL : 3UL);
913+
} else if (panel.expanded && pageNum < _lastPageNum && panel.activePage > 0) {
914+
panel.activePage = MAX(panel.activePage + pageNum - _lastPageNum, pageNum == 0 ? 0UL : 1UL);
915+
}
916+
}
917+
highlightedIndex += pageSize * panel.activePage;
918+
}
877919

878920
if (showingStatus) {
879921
[self clearBuffer];
@@ -909,7 +951,7 @@ - (void)rimeUpdate {
909951
}
910952
} else {
911953
if (_inlinePreedit && !_showingSwitcherMenu) {
912-
if (_inlinePlaceholder && preeditText.length == 0 && numCandidate > 0) {
954+
if (_inlinePlaceholder && preeditText.length == 0 && numCandidates > 0) {
913955
[self showPlaceholder:kFullWidthSpace];
914956
} else if (!didCommit || preeditText.length > 0) {
915957
[self showPreeditString:preeditText
@@ -925,20 +967,44 @@ - (void)rimeUpdate {
925967
}
926968
}
927969
// update candidates
928-
NSMutableArray *candidates = [[NSMutableArray alloc] initWithCapacity:numCandidate];
929-
NSMutableArray *comments = [[NSMutableArray alloc] initWithCapacity:numCandidate];
930-
for (NSUInteger i = 0; i < numCandidate; ++i) {
970+
NSMutableArray *candidates = numCandidates ? [[NSMutableArray alloc] init] : nil;
971+
NSMutableArray *comments = numCandidates ? [[NSMutableArray alloc] init] : nil;
972+
if (numCandidates > 0 && panel.expanded && panel.activePage > 0) {
973+
NSUInteger index = pageSize * (pageNum - panel.activePage);
974+
RimeCandidateListIterator iterator;
975+
if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) {
976+
NSUInteger endIndex = pageSize * pageNum;
977+
while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) {
978+
[candidates addObject:@(iterator.candidate.text)];
979+
[comments addObject:@(iterator.candidate.comment ? : "")];
980+
}
981+
rime_get_api()->candidate_list_end(&iterator);
982+
}
983+
}
984+
for (NSUInteger i = 0; i < numCandidates; ++i) {
931985
[candidates addObject:@(ctx.menu.candidates[i].text)];
932986
[comments addObject:@(ctx.menu.candidates[i].comment ? : "")];
933987
}
988+
if (numCandidates > 0 && panel.expanded && panel.activePage < 5) {
989+
NSUInteger index = pageSize * (pageNum + 1);
990+
RimeCandidateListIterator iterator;
991+
if (rime_get_api()->candidate_list_from_index(_session, &iterator, (int)index)) {
992+
NSUInteger endIndex = pageSize * (pageNum + 5 - panel.activePage);
993+
while (index++ < endIndex && rime_get_api()->candidate_list_next(&iterator)) {
994+
[candidates addObject:@(iterator.candidate.text)];
995+
[comments addObject:@(iterator.candidate.comment ? : "")];
996+
}
997+
rime_get_api()->candidate_list_end(&iterator);
998+
}
999+
}
9341000
[self showPanelWithPreedit:_inlinePreedit && !_showingSwitcherMenu ? nil : preeditText
9351001
selRange:NSMakeRange(start, end - start)
9361002
caretPos:_showingSwitcherMenu ? NSNotFound : caretPos
9371003
candidates:candidates
9381004
comments:comments
939-
highlightedIndex:(NSUInteger)ctx.menu.highlighted_candidate_index
940-
pageNum:(NSUInteger)ctx.menu.page_no
941-
lastPage:(BOOL)ctx.menu.is_last_page];
1005+
highlightedIndex:highlightedIndex
1006+
pageNum:pageNum
1007+
lastPage:isLastPage];
9421008
rime_get_api()->free_context(&ctx);
9431009
} else {
9441010
[self hidePalettes];

SquirrelPanel.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ typedef NS_ENUM(NSUInteger, SquirrelAppear) {
1414

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

35+
- (NSUInteger)candidateIndexOnDirection:(SquirrelIndex)arrowKey;
36+
3237
- (void)showPreedit:(NSString *)preedit
3338
selRange:(NSRange)selRange
3439
caretPos:(NSUInteger)caretPos

0 commit comments

Comments
 (0)