Skip to content

Commit 1344d7e

Browse files
authored
shortcut improvements: F1 font size; F1 on menu; Home/PageUp char (#274)
1 parent f7f86f3 commit 1344d7e

File tree

10 files changed

+142
-52
lines changed

10 files changed

+142
-52
lines changed

keycode/keycode.cpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <cstring>
33

44
static struct {
5-
uint32_t osxKeycode;
5+
uint16_t osxKeycode;
66
fcitx::KeySym sym;
77
} sym_mappings[] = {
88
// modifiers
@@ -197,7 +197,7 @@ static struct {
197197
};
198198

199199
static struct {
200-
uint32_t osxKeycode;
200+
uint16_t osxKeycode;
201201
char asciiChar;
202202
char shiftedAsciiChar;
203203
} char_mappings[] = {
@@ -255,6 +255,32 @@ static struct {
255255
{OSX_VK_SLASH, '/', '?'},
256256
};
257257

258+
static struct {
259+
fcitx::KeySym sym;
260+
uint16_t osxFunctionKey;
261+
} function_key_mappings[] = {
262+
{FcitxKey_Up, NSUpArrowFunctionKey},
263+
{FcitxKey_Down, NSDownArrowFunctionKey},
264+
{FcitxKey_Left, NSLeftArrowFunctionKey},
265+
{FcitxKey_Right,NSRightArrowFunctionKey},
266+
{FcitxKey_F1, NSF1FunctionKey},
267+
{FcitxKey_F2, NSF2FunctionKey},
268+
{FcitxKey_F3, NSF3FunctionKey},
269+
{FcitxKey_F4, NSF4FunctionKey},
270+
{FcitxKey_F5, NSF5FunctionKey},
271+
{FcitxKey_F6, NSF6FunctionKey},
272+
{FcitxKey_F7, NSF7FunctionKey},
273+
{FcitxKey_F8, NSF8FunctionKey},
274+
{FcitxKey_F9, NSF9FunctionKey},
275+
{FcitxKey_F10, NSF10FunctionKey},
276+
{FcitxKey_F11, NSF11FunctionKey},
277+
{FcitxKey_F12, NSF12FunctionKey},
278+
{FcitxKey_Home, NSHomeFunctionKey},
279+
{FcitxKey_End, NSEndFunctionKey},
280+
{FcitxKey_Page_Up, NSPageUpFunctionKey},
281+
{FcitxKey_Page_Down, NSPageDownFunctionKey},
282+
};
283+
258284
static struct {
259285
uint32_t osxModifier;
260286
fcitx::KeyState fcitxModifier;
@@ -328,21 +354,6 @@ std::string fcitx_keysym_to_osx_keysym(fcitx::KeySym keySym) {
328354
if (fcitx::Key{keySym}.isKeyPad()) {
329355
return "";
330356
}
331-
// TODO: VSCode Run has many functional key shortcuts, so we can copy
332-
// implementation from electron.
333-
switch (keySym) {
334-
// Hack for arrow
335-
case FcitxKey_Left:
336-
return "\u{1c}";
337-
case FcitxKey_Right:
338-
return "\u{1d}";
339-
case FcitxKey_Up:
340-
return "\u{1e}";
341-
case FcitxKey_Down:
342-
return "\u{1f}";
343-
default:
344-
break;
345-
}
346357
// keySymToString returns grave for `, which will be used as G by macOS.
347358
auto sym = fcitx::Key::keySymToUTF8(keySym);
348359
// Normalized fcitx key like Control+D will show and be counted as
@@ -353,6 +364,15 @@ std::string fcitx_keysym_to_osx_keysym(fcitx::KeySym keySym) {
353364
return sym;
354365
}
355366

367+
uint16_t fcitx_keysym_to_osx_function_key(fcitx::KeySym keySym) {
368+
for (const auto &pair : function_key_mappings) {
369+
if (pair.sym == keySym) {
370+
return pair.osxFunctionKey;
371+
}
372+
}
373+
return 0;
374+
}
375+
356376
uint32_t fcitx_keystates_to_osx_modifiers(fcitx::KeyStates ks) {
357377
uint32_t ret{};
358378
for (const auto &pair : modifier_mappings) {

keycode/keycode.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,34 @@
243243
#define OSX_MODIFIER_NUMERICPAD (1 << 21)
244244
#define OSX_MODIFIER_HELP (1 << 22)
245245
#define OSX_MODIFIER_FUNCTION (1 << 23)
246+
247+
// AppKit/NSEvent.h
248+
enum {
249+
NSUpArrowFunctionKey = 0xF700,
250+
NSDownArrowFunctionKey = 0xF701,
251+
NSLeftArrowFunctionKey = 0xF702,
252+
NSRightArrowFunctionKey = 0xF703,
253+
NSF1FunctionKey = 0xF704,
254+
NSF2FunctionKey = 0xF705,
255+
NSF3FunctionKey = 0xF706,
256+
NSF4FunctionKey = 0xF707,
257+
NSF5FunctionKey = 0xF708,
258+
NSF6FunctionKey = 0xF709,
259+
NSF7FunctionKey = 0xF70A,
260+
NSF8FunctionKey = 0xF70B,
261+
NSF9FunctionKey = 0xF70C,
262+
NSF10FunctionKey = 0xF70D,
263+
NSF11FunctionKey = 0xF70E,
264+
NSF12FunctionKey = 0xF70F,
265+
// Using Insert as keyEquivalent will result in ' wrongly used.
266+
// NSInsertFunctionKey = 0xF727,
267+
// fcitx_keysym_to_osx_keysym is responsible for Delete and Backspace.
268+
// NSDeleteFunctionKey = 0xF728,
269+
NSHomeFunctionKey = 0xF729,
270+
NSEndFunctionKey = 0xF72B,
271+
NSPageUpFunctionKey = 0xF72C,
272+
NSPageDownFunctionKey = 0xF72D,
273+
};
246274
// clang-format on
247275

248276
fcitx::KeySym osx_unicode_to_fcitx_keysym(uint32_t unicode,
@@ -257,5 +285,7 @@ fcitx::Key osx_key_to_fcitx_key(uint32_t unicode, uint32_t modifiers,
257285
// Used for showing shortcut configuration and setting shortcut for menu items.
258286
// No need to be complete and sometimes must be inaccurate (e.g. A -> a).
259287
std::string fcitx_keysym_to_osx_keysym(fcitx::KeySym);
288+
uint16_t fcitx_keysym_to_osx_function_key(fcitx::KeySym);
289+
260290
uint16_t fcitx_keysym_to_osx_keycode(fcitx::KeySym);
261291
uint32_t fcitx_keystates_to_osx_modifiers(fcitx::KeyStates ks);

src/config/keycode.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ func macKeyToFcitxString(_ key: String, _ modifiers: NSEvent.ModifierFlags, _ co
1616
return String(osx_key_to_fcitx_string(unicode, UInt32(modifiers.rawValue), code))
1717
}
1818

19-
func fcitxStringToMacShortcut(_ s: String) -> String {
19+
func fcitxStringToMacShortcut(_ s: String) -> (String, String?) {
2020
let key = String(fcitx_string_to_osx_keysym(s))
2121
let modifiers = NSEvent.ModifierFlags(rawValue: UInt(fcitx_string_to_osx_modifiers(s)))
2222
let code = fcitx_string_to_osx_keycode(s)
2323
if key.isEmpty && code == 0 {
24-
return s
24+
return (s, nil)
2525
}
2626
return shortcutRepr(key, modifiers, code)
2727
}

src/config/keyrecorder.swift

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,24 @@ private let codeMap = [
2525
0x24: "",
2626
0x31: "",
2727
0x30: "",
28-
// function
28+
// cursor
29+
0x7e: "",
30+
0x7d: "",
31+
0x7b: "",
32+
0x7c: "",
33+
0x74: "",
34+
0x79: "",
35+
0x73: "",
36+
0x77: "",
37+
// pc keyboard
38+
0x72: "",
39+
0x71: "",
40+
0x69: "",
41+
0x6b: "",
42+
]
43+
44+
// Separate them because in the menu their font size is smaller and we want the same behavior in recorder UI as well.
45+
private let functionCodeMap = [
2946
0x7a: "F1",
3047
0x78: "F2",
3148
0x63: "F3",
@@ -38,23 +55,11 @@ private let codeMap = [
3855
0x6d: "F10",
3956
0x67: "F11",
4057
0x6f: "F12",
41-
// cursor
42-
0x7e: "",
43-
0x7d: "",
44-
0x7b: "",
45-
0x7c: "",
46-
0x74: "",
47-
0x79: "",
48-
0x73: "",
49-
0x77: "",
50-
// pc keyboard
51-
0x72: "",
52-
0x71: "",
53-
0x69: "",
54-
0x6b: "",
5558
]
5659

57-
func shortcutRepr(_ key: String, _ modifiers: NSEvent.ModifierFlags, _ code: UInt16) -> String {
60+
func shortcutRepr(_ key: String, _ modifiers: NSEvent.ModifierFlags, _ code: UInt16) -> (
61+
String, String?
62+
) {
5863
var desc = ""
5964
if modifiers.contains(.control) { desc += "" }
6065
if modifiers.contains(.option) { desc += "" }
@@ -66,12 +71,17 @@ func shortcutRepr(_ key: String, _ modifiers: NSEvent.ModifierFlags, _ code: UIn
6671
desc += "" // Shift_L
6772
}
6873
if modifiers.contains(.command) { desc += "" }
74+
if let normalFont = codeMap[Int(code)] {
75+
return (desc + normalFont, nil)
76+
} else if let smallerFont = functionCodeMap[Int(code)] {
77+
return (desc, smallerFont)
78+
}
6979
// Use uppercase to match menu.
70-
return desc + (codeMap[Int(code)] ?? key.uppercased())
80+
return (desc + key.uppercased(), nil)
7181
}
7282

7383
struct RecordingOverlay: NSViewRepresentable {
74-
@Binding var recordedShortcut: String
84+
@Binding var recordedShortcut: (String, String?)
7585
@Binding var recordedKey: String
7686
@Binding var recordedModifiers: NSEvent.ModifierFlags
7787
@Binding var recordedCode: UInt16

src/config/optionviews.swift

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@ struct BooleanOptionView: OptionView {
2121
}
2222
}
2323

24+
func recordedKeyView(_ pair: (String, String?)) -> some View {
25+
let (normalFont, smallerFont) = pair
26+
if let smallerFont = smallerFont {
27+
return Text(normalFont) + Text(smallerFont).font(.caption)
28+
} else {
29+
return Text(normalFont)
30+
}
31+
}
32+
2433
struct KeyOptionView: OptionView {
2534
let label: String
2635
let overrideLabel: String? = nil
2736
@ObservedObject var model: KeyOption
2837
@State private var showRecorder = false
29-
@State private var recordedShortcut = ""
38+
@State private var recordedShortcut: (String, String?) = ("", nil)
3039
@State private var recordedKey = ""
3140
@State private var recordedModifiers = NSEvent.ModifierFlags()
3241
@State private var recordedCode: UInt16 = 0
@@ -35,11 +44,12 @@ struct KeyOptionView: OptionView {
3544
Button {
3645
showRecorder = true
3746
} label: {
38-
Text(model.value.isEmpty ? "●REC" : fcitxStringToMacShortcut(model.value)).frame(
39-
minWidth: 100)
47+
recordedKeyView(model.value.isEmpty ? ("●REC", nil) : fcitxStringToMacShortcut(model.value))
48+
.frame(
49+
minWidth: 100)
4050
}.sheet(isPresented: $showRecorder) {
4151
VStack {
42-
Text(recordedShortcut)
52+
recordedKeyView(recordedShortcut)
4353
.background(
4454
RecordingOverlay(
4555
recordedShortcut: $recordedShortcut, recordedKey: $recordedKey,

src/controller.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ private func repObjectIMK(_ sender: Any?) -> Any? {
329329

330330
struct FcitxKey: Codable {
331331
let sym: String
332+
let functionKey: UInt16
332333
let states: UInt
333334
}
334335

@@ -349,10 +350,19 @@ struct FcitxAction: Codable {
349350
return []
350351
}
351352

353+
var keyEquivalent = ""
354+
if let key = hotkey?[0] {
355+
if !key.sym.isEmpty {
356+
keyEquivalent = key.sym
357+
} else if key.functionKey != 0 {
358+
keyEquivalent = String(
359+
utf16CodeUnits: [unichar(key.functionKey)], count: 1)
360+
}
361+
}
352362
let item = NSMenuItem(
353363
title: String(repeating: "  ", count: depth) + desc,
354364
action: #selector(FcitxInputController.activateFcitxAction),
355-
keyEquivalent: hotkey?[0].sym ?? "")
365+
keyEquivalent: keyEquivalent)
356366
item.keyEquivalentModifierMask = NSEvent.ModifierFlags(rawValue: hotkey?[0].states ?? 0)
357367
item.target = target
358368
item.representedObject = self

src/fcitx.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,11 +436,13 @@ static nlohmann::json actionToJson(fcitx::Action *action,
436436
}
437437
for (const auto &key : action->hotkey()) {
438438
auto sym = fcitx_keysym_to_osx_keysym(key.sym());
439-
if (sym.empty()) {
439+
auto functionKey = fcitx_keysym_to_osx_function_key(key.sym());
440+
if (sym.empty() && functionKey == 0) {
440441
continue;
441442
}
442443
j["hotkey"].push_back(
443444
{{"sym", std::move(sym)},
445+
{"functionKey", functionKey},
444446
{"states", fcitx_keystates_to_osx_modifiers(key.states())}});
445447
}
446448
return j;

tests/testkey.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ void test_osx_to_fcitx() {
2727
}
2828

2929
void test_fcitx_to_osx() {
30-
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_Up) == "\u{1e}");
30+
FCITX_ASSERT(fcitx_keysym_to_osx_function_key(FcitxKey_Up) == 0xF700);
31+
FCITX_ASSERT(fcitx_keysym_to_osx_function_key(FcitxKey_F12) == 0xF70F);
32+
33+
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_Left) == "");
34+
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_F12) == "");
3135
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_0) == "0");
3236
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_KP_0) == "");
3337
FCITX_ASSERT(fcitx_keysym_to_osx_keysym(FcitxKey_grave) == "`");
@@ -47,7 +51,8 @@ void test_fcitx_to_osx() {
4751
}
4852

4953
void test_fcitx_string() {
50-
FCITX_ASSERT(fcitx_string_to_osx_keysym("Left") == "\u{1c}");
54+
FCITX_ASSERT(fcitx_string_to_osx_keysym("Left") == "");
55+
FCITX_ASSERT(fcitx_string_to_osx_keysym("F12") == "");
5156
FCITX_ASSERT(fcitx_string_to_osx_keysym("Control+0") == "0");
5257
FCITX_ASSERT(fcitx_string_to_osx_keysym("Control+Shift+KP_0") == "");
5358
FCITX_ASSERT(fcitx_string_to_osx_keysym("Control+slash") == "/");

tests/testkey.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ func testMacToFcitx() {
77
}
88

99
func testFcitxToMac() {
10-
assert(fcitxStringToMacShortcut("0") == "0")
11-
assert(fcitxStringToMacShortcut("KP_0") == "🄋")
12-
assert(fcitxStringToMacShortcut("Control+A") == "⌃A")
13-
assert(fcitxStringToMacShortcut("Control+Shift+A") == "⌃⇧A")
14-
assert(fcitxStringToMacShortcut("Shift+Super+Shift_L") == "⇧⌘")
15-
assert(fcitxStringToMacShortcut("Alt+Shift+Shift_R") == "⌥⬆")
10+
assert(fcitxStringToMacShortcut("0") == ("0", nil))
11+
assert(fcitxStringToMacShortcut("KP_0") == ("🄋", nil))
12+
assert(fcitxStringToMacShortcut("Control+A") == ("⌃A", nil))
13+
assert(fcitxStringToMacShortcut("Control+Shift+A") == ("⌃⇧A", nil))
14+
assert(fcitxStringToMacShortcut("Shift+Super+Shift_L") == ("⇧⌘", nil))
15+
assert(fcitxStringToMacShortcut("Alt+Shift+Shift_R") == ("⌥⬆", nil))
16+
assert(fcitxStringToMacShortcut("F12") == ("", "F12"))
17+
assert(fcitxStringToMacShortcut("Shift+F12") == ("", "F12"))
18+
assert(fcitxStringToMacShortcut("Super+Home") == ("⌘⤒", nil))
1619
}
1720

1821
@_cdecl("main")

0 commit comments

Comments
 (0)