Skip to content

Commit f3cd7a7

Browse files
committed
现在可以设置 Esc 在哪些应用中生效,默认 在vscode, terminal, obsidian, cursor, windsurf 中生效
参考 issue: #2
1 parent 89689e9 commit f3cd7a7

5 files changed

Lines changed: 207 additions & 51 deletions

File tree

AppDelegate.swift

Lines changed: 125 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,147 @@ import Cocoa
22

33
class AppDelegate: NSObject, NSApplicationDelegate, KeyboardManagerDelegate {
44
let statusBarManager = StatusBarManager()
5-
5+
// 存储允许的应用bundle identifiers
6+
var allowedApps: Set<String> = []
7+
// 用于存储系统中的应用列表
8+
var systemApps: [(name: String, bundleId: String)] = []
9+
610
func applicationDidFinishLaunching(_ notification: Notification) {
11+
print("应用程序开始初始化...")
12+
713
// 确保应用程序被激活
814
NSApp.activate(ignoringOtherApps: true)
15+
print("应用程序已激活")
916

1017
// 确保应用不会随终端退出
1118
ProcessInfo.processInfo.enableSuddenTermination()
19+
print("已启用突然终止")
20+
21+
// 加载默认配置
22+
loadDefaultConfig()
23+
print("已加载默认配置")
24+
25+
// 加载系统应用列表
26+
loadSystemApps()
27+
print("已加载系统应用列表,共 \(systemApps.count) 个应用")
1228

1329
// 设置代理
1430
KeyboardManager.shared.delegate = self
31+
statusBarManager.appDelegate = self
32+
print("已设置键盘管理器代理")
1533

16-
// 先设置状态栏
34+
// 设置状态栏和菜单
35+
print("开始设置状态栏和菜单...")
1736
DispatchQueue.main.async { [weak self] in
1837
self?.statusBarManager.setupStatusBarItem()
38+
print("状态栏和菜单设置完成")
1939
}
2040

21-
// 然后启动键盘管理器
41+
// 启动键盘管理器
42+
print("开始启动键盘管理器...")
2243
KeyboardManager.shared.start()
44+
print("键盘管理器启动完成")
2345

2446
// 显示初始使用提示
2547
DispatchQueue.main.async { [weak self] in
2648
self?.showInitialInstructions()
49+
print("初始使用提示已显示")
50+
}
51+
52+
print("应用程序初始化完成")
53+
}
54+
55+
// 加载默认配置
56+
private func loadDefaultConfig() {
57+
allowedApps = Set([
58+
"com.apple.Terminal",
59+
"com.microsoft.VSCode",
60+
"com.vim.MacVim",
61+
"com.exafunction.windsurf",
62+
"md.obsidian",
63+
"dev.warp.Warp-Stable",
64+
"com.todesktop.230313mzl4w4u92"
65+
])
66+
}
67+
68+
// 加载系统应用列表
69+
private func loadSystemApps() {
70+
let workspace = NSWorkspace.shared
71+
72+
// 获取常用应用程序目录
73+
let appDirs = [
74+
"/Applications",
75+
"~/Applications",
76+
"/System/Applications"
77+
].map { NSString(string: $0).expandingTildeInPath }
78+
79+
var apps: [(name: String, bundleId: String)] = []
80+
81+
// 遍历应用程序目录
82+
for dir in appDirs {
83+
if let contents = try? FileManager.default.contentsOfDirectory(atPath: dir) {
84+
for item in contents {
85+
if item.hasSuffix(".app") {
86+
let path = (dir as NSString).appendingPathComponent(item)
87+
if let bundle = Bundle(path: path),
88+
let bundleId = bundle.bundleIdentifier,
89+
let appName = bundle.infoDictionary?["CFBundleName"] as? String {
90+
apps.append((name: appName, bundleId: bundleId))
91+
}
92+
}
93+
}
94+
}
95+
}
96+
97+
// 添加当前正在运行的应用
98+
let runningApps = workspace.runningApplications
99+
for app in runningApps {
100+
if let bundleId = app.bundleIdentifier,
101+
let appName = app.localizedName,
102+
!apps.contains(where: { $0.bundleId == bundleId }) {
103+
apps.append((name: appName, bundleId: bundleId))
104+
}
105+
}
106+
107+
// 按应用名称排序
108+
systemApps = apps.sorted { $0.name < $1.name }
109+
}
110+
111+
@objc func toggleApp(_ sender: NSMenuItem) {
112+
guard let bundleId = sender.representedObject as? String else { return }
113+
114+
if allowedApps.contains(bundleId) {
115+
allowedApps.remove(bundleId)
116+
sender.state = .off
117+
} else {
118+
allowedApps.insert(bundleId)
119+
sender.state = .on
120+
}
121+
}
122+
123+
@objc func refreshAppList() {
124+
loadSystemApps()
125+
statusBarManager.createAndShowMenu()
126+
}
127+
128+
// 检查当前应用是否允许使用ESC切换输入法
129+
private func isCurrentAppAllowed() -> Bool {
130+
if let frontmostApp = NSWorkspace.shared.frontmostApplication {
131+
return allowedApps.contains(frontmostApp.bundleIdentifier ?? "")
27132
}
133+
return false
28134
}
29-
135+
136+
// 在切换输入法前检查当前应用
137+
func shouldSwitchInputSource() -> Bool {
138+
return isCurrentAppAllowed()
139+
}
140+
141+
// 实现代理方法
142+
func keyboardManagerDidUpdateState() {
143+
statusBarManager.updateStatusBarIcon()
144+
}
145+
30146
private func showInitialInstructions() {
31147
let alert = NSAlert()
32148
alert.messageText = "MacVimSwitch 使用说明"
@@ -36,34 +152,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, KeyboardManagerDelegate {
36152
2. 具体操作:打开输入法偏好设置 → 关闭"使用 Shift 切换中英文"
37153
38154
功能说明:
39-
1. 按 ESC 键会自动切换到英文输入法 ABC
155+
1. 按 ESC 键会自动切换到英文输入法 ABC(仅在指定的应用中生效)
40156
2. 按 Shift 键可以在中英文输入法之间切换(可在菜单栏中关闭)
41157
3. 提示:在 Mac 上,CapsLock 短按可以切换输入法,长按才是锁定大写
158+
159+
配置说明:
160+
1. 点击菜单栏图标 → 启用的应用,可以选择需要启用ESC切换功能的应用
161+
2. 如果没有看到某个应用,可以点击"刷新应用列表"更新
42162
"""
43163
alert.alertStyle = .warning
44164
alert.addButton(withTitle: "我已了解")
45165

46166
DispatchQueue.main.async {
47-
let window = NSWindow(
48-
contentRect: NSRect(x: 0, y: 0, width: 100, height: 100),
49-
styleMask: [.titled],
50-
backing: .buffered,
51-
defer: false
52-
)
53-
window.level = .floating
54-
55-
NSApp.activate(ignoringOtherApps: true)
56167
alert.runModal()
57168
}
58169
}
59-
60-
// 实现代理方法
61-
func keyboardManagerDidUpdateState() {
62-
statusBarManager.updateStatusBarIcon()
63-
statusBarManager.createAndShowMenu()
64-
}
65-
66-
func applicationWillTerminate(_ notification: Notification) {
67-
KeyboardManager.shared.disableEventTap()
68-
}
69170
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Download from [GitHub Releases](https://github.com/Jackiexiao/macvimswitch/relea
4242
- View instructions
4343
- Select your preferred Chinese input method
4444
- Enable/Disable Shift key switching
45+
- Select the applications that you want to enable `Esc` to switch input source, by default, vscode, terminal, obsidian, cursor, windsurf, warp are enabled.
4546
- Enable/Disable launch at login
4647
- Quit the application
4748

README_CN.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ MacVimSwitch 是一个 macOS 输入法切换工具,专为 Vim 用户和经常
4343
- 查看使用说明
4444
- 选择偏好的中文输入法
4545
- 开启/关闭 Shift 键切换功能
46+
- 选择 Esc 生效的应用(可多选,默认在 Terminal、VSCode、MacVim、windsurf、obsidian、warp 中生效)
4647
- 开启/关闭开机自动启动
4748
- 退出应用程序
4849

@@ -97,8 +98,13 @@ git push origin main
9798

9899
本地构建和测试:
99100
```bash
100-
swiftc macvimswitch.swift -o macvimswitch
101-
./macvimswitch
101+
./build.sh
102+
# 打开第一次,辅助功能先把之前的 MacVimSwitch 删除,看起来好像必须得这么做
103+
open dist/MacVimSwitch.app
104+
# 打开第二次,启动辅助功能
105+
open dist/MacVimSwitch.app
106+
# 打开第三次,相当于重启应用,这个时候你才能正确获取授权
107+
open dist/MacVimSwitch.app
102108
```
103109

104110
构建发布版本:
@@ -125,7 +131,7 @@ tccutil reset All com.jackiexiao.macvimswitch # Reset permissions
125131
MacVimSwitch 相比其他输入法切换方案有以下优势:
126132

127133
1. 通用兼容性
128-
- 可在所有应用程序中使用(VSCode、终端、Obsidian、Cursor 等)
134+
- 可在所有应用程序中使用(VSCode、终端、Obsidian、Cursor、Warp、Windsurf 等)
129135
- 无需针对不同应用进行配置
130136
- 不需要为不同编辑器安装插件
131137

StatusBarManager.swift

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import Cocoa
33
class StatusBarManager {
44
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
55
private var menu: NSMenu?
6-
6+
weak var appDelegate: AppDelegate?
7+
78
func setupStatusBarItem() {
89
if let button = statusItem.button {
910
updateStatusBarIcon()
@@ -13,7 +14,7 @@ class StatusBarManager {
1314
print("错误:无法创建状态栏按钮")
1415
}
1516
}
16-
17+
1718
func updateStatusBarIcon() {
1819
guard let button = statusItem.button else {
1920
print("Status item button not found")
@@ -28,7 +29,7 @@ class StatusBarManager {
2829

2930
button.isEnabled = true
3031
}
31-
32+
3233
func createAndShowMenu() {
3334
let newMenu = NSMenu()
3435

@@ -63,6 +64,31 @@ class StatusBarManager {
6364
newMenu.addItem(inputMethodItem)
6465
newMenu.addItem(NSMenuItem.separator())
6566

67+
// 添加应用列表子菜单
68+
if let delegate = appDelegate {
69+
let appsMenu = NSMenu()
70+
let appsMenuItem = NSMenuItem(title: "Esc生效的应用", action: nil, keyEquivalent: "")
71+
appsMenuItem.submenu = appsMenu
72+
73+
// 添加所有应用到子菜单
74+
for app in delegate.systemApps {
75+
let item = NSMenuItem(title: app.name, action: #selector(AppDelegate.toggleApp(_:)), keyEquivalent: "")
76+
item.state = delegate.allowedApps.contains(app.bundleId) ? .on : .off
77+
item.representedObject = app.bundleId
78+
item.target = delegate
79+
appsMenu.addItem(item)
80+
}
81+
82+
// 添加刷新应用列表选项
83+
appsMenu.addItem(NSMenuItem.separator())
84+
let refreshItem = NSMenuItem(title: "刷新应用列表", action: #selector(AppDelegate.refreshAppList), keyEquivalent: "r")
85+
refreshItem.target = delegate
86+
appsMenu.addItem(refreshItem)
87+
88+
newMenu.addItem(appsMenuItem)
89+
newMenu.addItem(NSMenuItem.separator())
90+
}
91+
6692
// 修改 Shift 切换选项的文字
6793
let shiftSwitchItem = NSMenuItem(
6894
title: "使用 Shift 切换入法",
@@ -86,35 +112,39 @@ class StatusBarManager {
86112
newMenu.addItem(launchAtLoginItem)
87113

88114
newMenu.addItem(NSMenuItem.separator())
89-
newMenu.addItem(NSMenuItem(title: "退出", action: #selector(quitApp), keyEquivalent: "q"))
115+
116+
// 添加退出选项
117+
let quitItem = NSMenuItem(title: "退出", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
118+
quitItem.target = NSApp
119+
newMenu.addItem(quitItem)
90120

91121
statusItem.menu = newMenu
92122
self.menu = newMenu
93123
}
94-
124+
95125
@objc private func openHomepage() {
96126
if let url = URL(string: "https://github.com/Jackiexiao/macvimswitch") {
97127
NSWorkspace.shared.open(url)
98128
}
99129
}
100-
130+
101131
@objc private func toggleShiftSwitch() {
102132
KeyboardManager.shared.useShiftSwitch = !KeyboardManager.shared.useShiftSwitch
103133
updateStatusBarIcon()
104134
createAndShowMenu()
105135
}
106-
136+
107137
@objc private func selectInputMethod(_ sender: NSMenuItem) {
108138
guard let sourceId = sender.representedObject as? String else { return }
109139
KeyboardManager.shared.setLastInputSource(sourceId)
110140
createAndShowMenu()
111141
}
112-
142+
113143
@objc private func toggleLaunchAtLogin() {
114144
LaunchManager.shared.toggleLaunchAtLogin()
115145
createAndShowMenu()
116146
}
117-
147+
118148
@objc private func quitApp() {
119149
KeyboardManager.shared.disableEventTap()
120150
NSApplication.shared.terminate(self)

0 commit comments

Comments
 (0)