作为一个 OS X 和 iOS 用户,我想你对 PopClip 和 Command-C 这两款应用应该不会感到陌生。

前者能够给 OS X 带来像 iOS 上类似的文字复制粘贴体验,省却了在键盘与鼠标之间频繁的切换。并且拥有大量优秀的扩展插件,能够实现各种各样的功能,其中总会有那么几款能够直击你的需求。最为强大的是即使没有能够满足你需求的插件,它还支持自己编写插件。详细的介绍可以参考少数派的这篇文章《没那么简单:PopClip for Mac》,里面有比较全面的功能介绍。

而后者则可以实现 OS X 与 iOS 共享剪切板,为了让 Command-C 能够在 iOS 平台实现后台剪切板共享功能,开发者专门架设了一台服务器用来唤醒在 iOS 后台的 Command-C。因此 Command-C 在 iOS 后台运行时,如果网络状态不佳或者没有连接互联网的状态下,Command-C 在后台则无法正常工作,即使设备都在同一局域网下。但是在前台的状态下无需互联网亦可正常工作。

Command-C for PopClip插件实现的功能与原理

1. 插件功能

在一台安装有 PopClip 和 Command-C 的 OS X 设备上,要将一段文字或者一个网址分享到另外一个 OS X 或者 iOS 设备上的话,通常的操作是先选中文字然后使用 PopClip 自带的复制插件或者使用键盘上的「⌘+C」将文本复制后,然后用鼠标或者「⌘+⇧+x」激活 Command-C 面板,然后选择需要分享的设备。

而我们编写的这个 PopClip 扩展插件要现实的功能非常的简单却非常的实用,选中文字后点击 PopClip 气泡菜单上想要分享的设备,即可一键将选中的内容分享到 Command-C 中的设备。以后除了配置设备以外几乎不再需要对 Menu Bar 上的 Command-C 面板进行操作。

大多数情况下,大家一般都是拥有一台 Mac 加上一台 iPhone 或 iPad,因此插件默认配置是你的 Command-C 列表里只有 2 个设备,一个为 iPhone,另外一个为 iPad。如果你只有一个设备的话可以在插件的选项里开启或关闭其中任一设备。如果你的设备多于 2 个的话,则可以参考下面的教程,自行在插件里添加。


▲ 笔者编写的 Command-C for PopClip 插件截图

如果你对编写对详细过程并不感兴趣,可直接在文字末尾找到这个插件到下载链接。

2. 插件原理

用最通俗易懂的讲法就是:在编写的 PopClip 插件里放置一段能够激活 Command-C 并且让它自动地将保存在剪切板里的内容分享到相应设备的代码。

一听到代码可能你就觉得这个又是个高深莫测的东西,其实不完全是这样的。我们这里用到的脚本语言是 AppleScript,它是苹果专门针对 OS X 普通用户准备的一种自然脚本语言,利用它配合上 OS X 里的 Automator 能够实现各种自动化的工作流程。之所以称之为自然脚本语言,是因为它的语法不管是编程的简易性还是可读性都非常的高,比如 set Content to clipboard as text 这段代码的意思就是将当前剪切板中的内容以文本的形式分配给「Content」这个变量,看起来就是一句简单的英语而已。因为 AppleScript 是基于用户界面自动化开发的,所以在文件的操作效率上并没有 Shell Script 来的高。因此我们需要配合这两种脚本语言的特点,编写这个 PopClip 插件。

顺带一提,如果你只是需要实现一些简单功能的 PopClip 扩展,可以参考 PopMaker 这款小应用,无需任何的编程基础就可以创建简单的扩展插件,比如设置常用的 Markdown 语法。

▲ PopMaker 截图

开始编写插件

为了实现这个插件,OS X 或者 iOS 系统分别需要安装以下应用:

  • PopClip for Mac

  • Command-C for Mac

  • Command-C for iOS

 

1. 插件的文件构成

PopClip 的插件主要由三个部分组成:

  • Config.plist (属性列表文件)这个文件里注册了插件的一切信息,包括插件名称、版本要求、图标文件名、脚本文件名等数据,就像是插件的数据库。PopClip 并不会直接读取其他文件的信息,而是间接地通过属性列表文件间接获取它们的信息。在一些简单的插件中甚至无需脚本文件,单凭「plist」就能实现所有的功能。比如 PopClip Extensions 页面里的「Paste and Enter」插件就是只依靠「plist」文件。我们这个插件的大部分功能也是通过这个文件来实现的,因此会着重介绍这个部分。

  • Icon.png 图标文件并不是必须的,即使没有也不会影响到插件的运行。文件名可以任意,但是必须匹配 Config.pilst 中设定的名称,否则会造成插件的图标无法显示。为了更好的辨别设备,在我们这个插件里使用到了两个图标文件,一个作为 iPhone 的图标,另外一个作为 iPad 的图标。

  • Code.AppleScript 「AppleScript」的脚本文件,里面包含了插件的代码部分。PopClip 支持多种语言编写的脚本文件,如果使用 Shell Script 编写脚本代码的话,那么扩展名则是「.Sh」。脚本文件的名称也可以任意命名,但是也必须和 Config.plst 文件里设定的相匹配。一般情况下一个插件只需要一个脚本文件,但是这并不意味着只能拥有一个脚本文件。我们这里为了实现将剪切板内容分享到不同的设备中,就用到了两个脚本文件,一个是将剪切板内容分享到 iPhone,另外一个则是分享到 iPad。

知道了插件的主要文件构成之后,我们按照顺序逐步创建。

2. 创建 Config.plist 文件

使用系统自带的 TextEdit 新建一个纯文本文件(.txt),然后复制[这个页面]里的代码到文本文件中。需要注意的是 TextEdit 默认新建的文件格式是富文本格式(.RTF),要新建纯文本格式(.TXT)文档的话,在 TextEdit 的偏好设置里需要先将默认格式勾选为纯文本文件,再重新打开以生效。完成操作后保存文件,并修改文件名为 Config.plist,在出现的对话框中选择使用「.plist」扩展名。完成后打开文件应该如图所示:

PopClip 的属性列表文件(.plist)拥有众多丰富灵活的参数,因为篇幅的原因我们这里只着重介绍本文会使用到的参数,基本上已经囊括了最常用的部分。如果有兴趣的话可以查看 PopClip 在 GitHub 上的官方文档,里面有所有参数的详细使用方法和介绍。

属性列表文件在纯文本模式下是以缩进的方式来表示层级嵌套。如果想让 Plist 文件看起来更加有条理和层次的话,可以下载 Plistedit Pro 这款专业的 Plist 编辑器,它有一定期限的试用期。

▲ Plistedit Pro 界面

3. 参数解释

在解释参数之前最好把刚才编辑好的 plist 文件打开放在一旁比照参考。我们按照参数在 plist 文件中的出现顺序以及嵌套的关系,从上往下进行解释。

Actions 在属性列表文件中如果你拥有多个 Action 参数的话,那么 PopClip 的气泡菜单中就会出现相应个数的操作按钮。在这个例子中我们用到了两个 Action 参数,第一个是将剪切板内容分享到 iPhone,第二个则是将剪切板内容分享到 iPad。而每个 Action 的参数中又分别的嵌套了以下系列参数。

  • AppleScript File 这个参数的值标识着我们在 PopClip 气泡菜单中激活该 Action 的话,那么 PopClip 将会执行该值相对应的脚本文件。因此该参数的值应该和我们后面创建的脚本文件的文件名必须保持一致性否则整个插件都无法运行。

  • Before 这个参数与之相对应的是 After 参数,功能是在运行脚本文件之前或者之后执行某个特定的操作。这个操作可以是 copy、cut 或者 paste 等。我们这里用到的是 copy ,也就是说在运行脚本代码之前将选中的文字复制到剪切板中。

  • Image File 该 Action 在 PopClip 气泡菜单上显示的图标文件的文件名,为空的话即使用标题来替代图标显示在气泡菜单上。

  • Long Running 这个参数对应的数值分别是 true 和 false 。如果为 ture 则在运行插件时,PopClip 会显示一个加载动画。建议在创建代码比较复杂的插件或者需要开启大型程序来完成插件所实现的功能时启用该选项。

  • Title 显示标题,在没有指定图标文件时该数值会显示在 PopClip 的气泡菜单上,而在有指定图标文件的情况下,将鼠标放置在该 Action 上该数值就会起悬浮窗的形式出现在鼠标旁。

Extension Description 插件的描述,用来该插件会实现怎样的功能,这个参数并不会实际影响到插件的运行。

Extension Identifier 插件的唯一标识,必须独一无二。如果两个插件实现的功能天壤之别,而该参数的值是相同的。在安装后者的时候会自动将前者覆盖掉,因为 PopClip 是通过该参数的值来辨别插件的。

Extension Name 插件名称。这个和上面的 Title 有些不同,这里的名称指的是 PopClip 的插件配置界面下显示的名称。

Options 这里用到了 4 个 Option 参数,前两者以文本输入框的形式,让用户输入设备在 Command-C 中的顺序以便正常的显示与设备相对应的图标。后两个 Option 参数则是以复选框的形式出现在插件配置菜单中,如果你的设备只有一个的话,可以通过复选框启用或关闭其中任一设备。

在安装插件的时候 PopClip 会弹出选项要求你设定设备在 Command-C 中的排序号。如果设定后想再次修改的话,可以点击状态栏上的 PopClip 图标,然后切换到扩展标签页,点击左下角的铅笔图标,最后选择我们创建的插件边上的齿轮图标,就可以再次设定。需要注意的是如果 iPhone 在 Command-C 中是排序第一个设备,那么应该输入数值「0」而非「1」,在第二个设备则是「1」而非「2」,依此类推。Options 下又嵌套以下参数:

  • Option Identifier 选项的唯一标识。这个参数的值也是必须独一无二的。同时这个参数的值也是一个环境变量。{popclip option *} ,只要将其中的星号替换成 Option Identifier 的值则为该环境变量名。比如第一个 Option Identifier 的环境变量即为 {popclip option iphonepositon} ,你在插件的选项页面里的第一个选项中输入的任何文本都会作为值赋予给这个环境变量。

  • Option Label 选项的标签名会显示在 PopClip 的插件的选项页面中。

  • Option Type 选项类型。我们这里用到了两种最常用的类型,一种是 string 另外一种是 boolean。如果是 string ,界面上的表现是在插件的选项界面下会出现一个文本框,在文本框内输入的文本会被作为值赋予给同一层级 Option Identifier 的环境变量。以我们这里第一个选项的 Option Identifier 值「iphoneposition」为例,它的类型是 string , Option Lable 为 iPhone Position 。那么在插件的选项界面下就会出现一个标题为 iPhone Postion 的文本输入框,而你在文本输入框里输入的文本内容会被当作值赋予给 {popclip option iphoneposition} 这个环境变量。第二种类型 boolean ,这种类型只有「0」和「1」两种值分别代表「Yes」和「No」,在用户界面上的表现是一个复选框选项。我们以第三个选项为例,第三个选项的参数 Option Identifier 值为「iphone-enabled」,标题为 iPhone Enabled 。那么如果我们勾选 iPhone Enabled 这个选项就会给环境参数 {option-iphone-enabled} 赋值为「1」,取消勾选则为「0」。

  • Option Default Value 就是赋予给 Option Identifier 环境变量的默认值。

至此本文中 Plist 里所用到的所有参数的意义和用法都介绍完了。

4. 图标文件

图标文件可以选择自己喜欢的图案。需要注意的地方是将需要显示的地方处理成纯黑色,不需要显示的地方删除掉像素保持透明状态,然后保存成正方形的 PNG 格式图片文件。PNG 可以很好的保留透明通道,为了显示效果图标的像素至少在 256 x 256 以上。在上面我们已经提过为了图标的正常显示,图标的文件名应该和 Image File 参数的值保持一致性。

参考图:

5. AppleScript 脚本文件

AppleScript 脚本文件中包含了插件的代码部分。一个插件要实现怎样的功能基本上都是取决于 AppleScript 脚本文件中的代码。为了实现将剪切板的内容分享到两个不同的设备,我们需要创建两个脚本文件。按照创建属性列表文件同样的方法,使用 TextEdit 创建一个纯文本文件,复制以下代码到文本文件中,并保存:


tell application "System Events"

    do shell script "open 'command-c://x-callback-url/copy?&deviceIndex={popclip option iphoneposition}'"

end tell

保存后,为了要和 Config.plist 中第一个 Action 嵌套下的参数 AppleScript File 的值保持匹配,我们将文件命名为 iphone.applescript 。如果出现对话框询问是否修改扩展名,选择使用 .applescript 。

使用同样的方法创建另外一个脚本文件,代码如下:


tell application "System Events"

    do shell script "open 'command-c://x-callback-url/copy?&deviceIndex={popclip option ipadposition}'"

end tell

将文件保存后,修改文件名为 ipad.applescript 。

对比上下两段代码几乎是一致的,唯一的区别就是末尾的环境变量。前者是将剪切板中的内容分享到 iPhone 中去,后者则是分享到 iPad 中。{popclip option ipadposition} 和 {popclip option ipadposition} 这两个环境变量的值来自于插件的选项页面中用户所输入的值,因此如果用户在插件的选项页面中颠倒输入设备的序号或者随意的输入其他文本,就会造成插件显示在 PopClip 气泡菜单上设备的图标错乱显示或者导致整个插件根本无法正常运行。

用过 Launch Center Pro 的同学在看到段代码时可能会有种似曾相识的感觉,实际上和我们使用在 Launch Center Pro 中的 URL Scheme 是一样的。只不过在这段里「deviceIndex」参数的值使用了 PopClip 提供的两个环境变量。在 iOS 中我们用 Launch Center Pro 来打开 URL,而在这里调用了 Shell Script 的「Open」命令来执行 URL。编辑完成后使用 AppleScript Editor 打开任意一个脚本文件,应该和下图是一致的:

6. 打包插件

将我们上面创建的几个文件放置在同一个文件夹中,然后修改该文件夹名称为 Command-c.popclipext ,弹出的对话框中确认使用 .popcliptext 扩展名。双击打开打包好的插件并确认安装。整个插件的编写就完成了。

[点击这里]可下载已经打包好的 Command-C for PopClip 插件(.zip格式,插件的源文件以及代码也已经发布到了 GitHub 上面。

后记

如果你想要实现更为复杂更为贴近自己需求的插件的话,我建议你可以参考一下 PopClip 官方在 GitHub 上的这个页面,官方网站上 PopClip Extensions 页面里所有插件的源文件以及源代码都包含在这个页面里。

有人看过 《Launch Center Pro进阶教程:实现iOS与Windows剪切板同步》 后询问在 OS X 下有没有类似的解决方案。本来我想在这里顺带介绍如何用 PopClip 配合 Dropbox 实现同样的功能,也算是一种补全。不幸的是现在要在天朝使用 Dropbox 首先你得…… 所以这里就不再介绍,直接点击这里下载 PopClip 配合 Dropbox 同步剪切板的插件。插件中我将 路径设定为 Apps/Launch Center Pro/Dropclip.txt,因此其他设备中的路径也必须保持一致,或者你可以自行打开插件里的 AppleScript 文件修改其中的路径。

如果你有任何疑问,欢迎在评论中留言讨论。