用Alfred实现“指哪打哪”的窗口切换

TL;DR

本文讲述如何用Alfred实现更高效的窗口切换,Workflow地址:https://github.com/jxq0/alfred-app-switcher

效果如下图所示,按下F1切换到Firefox,F2切换到Emacs,F3切换到iterm2。可以在Workflow中自定义快捷键与对应的App。

窗口管理:平铺式 vs 堆叠式

窗口切换是操作系统中最基本的操作,但不管是Mac还是Windows,切换的效率却都很低下,打开的窗口越多,切换的效率也越低。

两个系统的切换操作没有什么差别,按下快捷键出现选择窗口的切换条。切换条类似于一个队列,选中某个窗口后,它就会排到最前面。如果只需要在最近的两个窗口之间来回切换,只需按一次 CMD + Tab 就可以。如果需要在多个app之间切换,就得按多次快捷键,直到选中需要的窗口。

早些年玩Ubuntu桌面版的时候,用过一段时间的XMonad。XMonad是一个平铺式窗口管理器,“平铺式”顾名思义就是把所有的窗口都铺开,占据所有的屏幕空间,并且窗口之间不会互相覆盖。在这种方式下,用户可以自由定义窗口如何分布。所有窗口相关的操作都通过键盘完成。具体的使用可以参考阮一峰的《窗口管理器 xmonad 教程》

这种窗口管理的方式虽然非常高效,但对普通用户来说,门槛实在太高了。因此Mac和Windows都采用了上述这种更简单的也更适合普通用户的堆叠式窗口管理。这种窗口切换方式给我的感受就是大脑在飞速运转,但是双手操作速度却跟不上,大脑还要停下来等一等。在被迫忍受这种低效长达数年之后,我意识到自己需要一种“指哪打哪”的窗口切换方式:给常用的窗口分配不同的快捷键,只按一次快捷键就可以直接切换到对应的窗口。

Manico 与 Alfred

最开始选择的是Manico这个App,一直用的挺好,直到换了带Touchbar的MacBook。我的习惯是把F1-F12分配给具体的应用,但是Touchbar的功能键在Manico里不起作用,所以放弃了,也不知道现在这个Bug有没有修复。

如果你已经购买了Alfred的Powerpack,其实没必要再单独购买Manico。因为Alfred本身就可以实现切换窗口的操作。

如上图所示这样配置一个简单的workflow,即可实现按下快捷键直接切换到对应App的窗口,再次按下隐藏该窗口。用户可以结合自己的使用习惯,自由地配置快捷键和App。

为什么还需要alfred-app-switcher这个Workflow?

虽然以上workflow已经实现了指哪打哪的切换,但用起来还不是那么的顺手:

  1. 修改快捷键和App的映射关系不方便,需要打开Alfred的Workflow窗口进行修改。

  2. 映射关系并非一成不变。比如在看spark源代码的时候,需要使用IntelliJ Idea,但过段时间又不需要了。如果不修改映射关系,经常容易误触快捷键。

  3. 公司一台电脑,家里一台,用git同步Alfred的配置。但这两台电脑的使用场景是天然有差异的,比如在公司经常需要使用腾讯会议,在家使用的频率就非常低。无法共用一套Alfred配置。

Workflow实现细节

我们只需要Alfred的两个能力:一是用快捷键触发Workflow,二是切换到对应App的窗口,Alfred不需要感知映射关系。因此可以把映射关系写到配置文件中,然后通过脚本读取配置文件,并根据当前触发的快捷键输出对应App的名称。修改配置文件比打开Alfred方便多了。

针对映射关系变化的场景,可以通过多个Profile的方式来解决,不同的Profile下有各自的映射关系,映射关系变化时只需要切换Profile即可。

既然我们可以自己控制配置文件,那么多台机器使用场景的问题也就迎刃而解了。不同的机器使用不同的配置文件,这多份配置都可以同步到Git上。最终只需要在各个机器上创建软连接,指向各自的配置文件就可以了。

架构如下图所示,非常的简单。

配置文件

{
  "currentProfile": "default",
  "profiles": {
    "default": {
      "F1": "firefox",
      "ALTF1": "chrome",
      "F2": "emacs",
      "F3": "iterm",
      "ALTF4": "weworks",
      "F5": "pdfexpert",
      "F8": "ulysses",
      "F9": "dash",
      "F10": "wechat",
      "F12": "qq"
    },
    "work": {
      "ALTF1": "firefox",
      "F1": "chrome",
      "F5": "meeting",
      "F4": "weworks"
    },
    "idea": {
      "F4": "idea"
    }
  }
}

配置的结构非常清晰,应该一眼就可以看懂。 profiles 中定义了多个profile,其中 default 是基线配置,其他profile在基线基础上定义新的映射关系。当前生效的profile是 currentProfile

脚本

脚本最开始用python实现,后来用rust重写了。支持以下几个子命令:

  1. get-app ,根据快捷键查找对应的app

  2. list-profiles ,列出当前所有的profile

  3. detail ,输出指定profile的映射关系详情

  4. change-profile ,更改profile

Workflow

左边部分用于完成窗口切换,右边用于切换profile。由于无法直接控制alfred切换到对应的app,所以需要通过图中这个绿色条件分支控件连接到对应的app上。好在常用的app列表基本不会变化,这部分也就基本不需要修改。

写在最后

通过Alfred的usage可以看到每天大概需要切换几百次,节省了不少的时间,更重要的是,手总算能跟的上脑的速度了。