macOS下的Input Method Kit(译)

(本文来自《Input Method Kit API Reference》
Input Method Kit用于开发输入法,并管理与客户端应用之间的通讯,管理候选窗口和输入模式等。

概要

OSX 在10.5版本中引入了IMK,该框架提供了输入法开发的原型接口,与老版本的mac相比,这套接口大大简化了输入法开发的工作量。该框架与文字服务管理器充分整合,使得32位和64位应用程序和输入法之间可以无缝合作。

IMK通过几个类和协议来支持输入法与客户端应用间的通讯,并管理候选窗和输入模式等。输入法从转换引擎提供转换后的文字(该引擎可以用C,C++,Objective-C,Python等编写),还会提供键盘绑定、可选的事件处理、通过一个扩展的Info.plist文件提供更多输入法信息。还可以通过菜单项支持输入法的特殊命令或偏好设置。

接下来看IMK涉及的类、协议和其它数据结构。

IMKCandidates

IMKCandidates类用来表示候选。在用户选择了一个候选后,会通知对应的IMKInputController对象。候选是对已经输入的文字序列转换后的结果。IMKCandidates类支持在你的输入法中使用一个候选窗口来展现内容。该类的使用是可选的,不是所有输入法都需要它。

当创建一个IMKCandidates对象,你需要将它归属给输入法的IMKServer对象。然后重写IMKInputController的方法candidateSelectionChanged:candidateSelected:,并在你的代理对象中实现一个候选方法。IMKInputController子类通过实现候选方法来为IMKCandidate对象提供候选。当需要显示候选窗时,可以调用候选方法来更新候选列表并显示候选窗口。该类涉及的方法如下:

初始化候选窗

init!(server: IMKServer!, panelType: IMKCandidatePanelType)
  返回初始化的IMKCandidates对象

管理选择按键

func setSelectionKeys([Any]!)
  为候选设置选择键

func selectionKeys()
  返回一个NSNumber对象的数组,每个元素代表一个虚拟键盘码

func setSelectionKeysKeylayout(TISInputSource!)
  设置用来映射虚拟键盘码到字符的键盘布局

func selectionKeysKeylayout()
  返回用来映射虚拟键盘码到字符的键盘布局

管理窗口展现和行为

func show(IMKCandidatesLocationHint)
  显示候选窗

func hide()
  隐藏候选窗

func isVisible()
  返回候选窗是否可见

func setDismissesAutomatically(Bool)
  设置候选窗是否自动取消的标志

func dismissesAutomatically()
  返回候选窗是否自动取消的标志

func update()
  更新候选窗上的候选列表

管理窗体类型和文字属性

func panelType()
  返回候选窗的风格

func setPanelType(IMKCandidatePanelType)
  设置候选窗风格

func setAttributes([AnyHashable : Any]!)
  设置候选窗属性

func attributes()
  返回候选窗属性

显示注释窗口

func showAnnotation(NSAttributedString!)
  在注释窗口上显示注释串

常量

IMKCandidatePanelType
  IMK提供的候选窗口的类型

IMKCandidatesLocationHint
  放置候选窗口的位置提示

IMKCandidatesOpacityAttributeName
  候选窗口的透明度级别

初始化器

init!(server: IMKServer!, panelType: IMKCandidatePanelType, styleType: IMKStyleType)

实例方法

func attachChild(IMKCandidates!, toCandidate: Int, type: IMKStyleType)

func candidateFrame()

func candidateIdentifier(atLineNumber: Int)

func candidateStringIdentifier(Any!)

func clearSelection()

func detachChild(Int)

func hideChild()

func lineNumberForCandidate(withIdentifier: Int)

func selectCandidate(Int)

func selectCandidate(withIdentifier: Int)

func selectedCandidate()

func selectedCandidateString()

func setCandidateData([Any]!)

func setCandidateFrameTopLeft(NSPoint)

func show()

func showChild()

func showSublist([Any]!, subListDelegate: Any!)

IMKInputController

IMKInputController为用户自定义的输入控制器提供一个基类。由IMKServer为每个输入会话创建输入控制器,IMKServer通常在main函数的开头创建,输入会话则由客户端应用负责创建。对于每个输入会话都会有一个对应的输入控制器即IMKInputController对象。

IMKInputController对象用纸输入法侧的文本输入。它管理来自应用程序的事件和文本以及被输入法引擎转换后的文本。IMKInputController完全实现了IMKStateSetting协议和IMKMouseHandling协议。通常你不需要覆写该类,而只需要为你感兴趣的方法实现代理对象。IMKInputController协议方法的版本会检查代理对象是否实现了某方法,如果存在则调用此代理版本。以下是它的成员:

初始化InputController

init!(server: IMKServer!, delegate: Any!, client: Any!)
  通过设置代理,初始化输入控制器

区域相关

func compositionAttributes(at: NSRange)
  返回文本属性的字典

func selectionRange()
  返回选中文本的区域,该区域的文本会被选中且替换掉

func replacementRange()
  返回客户端文档中应当被替换掉的文本的区域

func mark(forStyle: Int, at: NSRange)
  返回文本属性的字典,这些属性可以用来标注一段属性串的区域并发送给客户端应用

管理代理

func setDelegate(Any!)
  为输入法控制器对象设置代理

获得客户端和服务端对象

func server()
  返回管理输入控制器的server对象

func client()
  返回与输入控制器关联的client对象

跟踪选择

func annotationSelected(NSAttributedString!, forCandidate: NSAttributedString!)
  向输入控制器发送被选中的候选串和注释串

func candidateSelectionChanged(NSAttributedString!)
  通知输入控制器当前候选窗中选中的候选项发生了变化

func candidateSelected(NSAttributedString!)
  通知输入控制器一个新的候选被选中了

管理写作串

func updateComposition()
  通知输入控制器写作串发生变化

func cancelComposition()
  取消当前写作串,并用原始串替代当前标记的文本

隐藏用户界面

func hidePalettes()
  通知输入法隐藏用户界面

用户自定义命令

func doCommand(by: Selector!, command: [AnyHashable : Any]!)
  传递由非输入法进程产生的命令

func menu()
  返回输入法的命令菜单

实例方法

func delegate()

func inputControllerWillClose()

IMKServer

IMKServer类管理客户端与输入法的连接。应当在main函数中创建IMKServer对象,永远不要修改该类,不要从它派生子类。下面是它的成员:

初始化对象

init!(name: String!, bundleIdentifier: String!)
  根据plist文件提供的信息创建并返回一个server对象

init!(name: String!, controllerClass: AnyClass!, delegateClass: AnyClass!)
  根据参数创建并返回一个server对象

获得输入法的Bundle

func bundle()
  返回输入法的NSBundle对象

常量

IMKModeDictionary
  输入法模式字典关键字

IMKControllerClass
  输入法控制器类的关键字

IMKDelegateClass
  输入法代理类的关键字

实例方法

func lastKeyEventWasDeadKey()

func paletteWillTerminate()

IMKMouseHandling

这是一个协议,它定义了输入法可以实现的鼠标处理事件。一下是它的成员:

处理鼠标事件

func mouseDown(onCharacterIndex: Int, coordinate: NSPoint, withModifier: Int, continueTracking: UnsafeMutablePointer!, client: Any!)
  处理发送给输入法的鼠标按下事件

func mouseUp(onCharacterIndex: Int, coordinate: NSPoint, withModifier: Int, client: Any!)
  处理发送给输入法的鼠标抬起事件

func mouseMoved(onCharacterIndex: Int, coordinate: NSPoint, withModifier: Int, client: Any!)
  处理发送给输入法的鼠标移动事件

IMKServerInput

这是一个协议,它定义了处理文本事件的方法。这不是一个正式协议,因为有三种方式来处理事件,输入法选择其中之一来实现对应方法。如下:

  • 键盘绑定。系统把每一个键盘按下事件映射到到输入法的一个方法。如果成功(找到此映射方法),系统调用didCommandBySelector:client:,否则(没有找到该方法)调用inputText:client:。针对此方式,你应当实现input​Text:​client:​did​Command​By​Selector:​client:​两个方法。

  • 只处理文本数据。这种方式下你无需键盘绑定就能接收到所有键盘事件,然后解析相关的文本数据。键盘事件会包含Unicodes,产生它们的键盘码,修饰标记。该数据被发送给方法input​Text:​key:​modifiers:​client:​,你应当实现该方法。

  • 处理所有事件。这种方式下输入法会接收到来自文本服务管理器的所有方法,这些方法被封装为NSEvent对象。你必须实现方法handle​Event:​client:​

支持键盘绑定

- (BOOL)inputText:(NSString *)string client:(id)sender;
  处理没有映射到响应方法的键盘按下事件

- (BOOL)didCommandBySelector:(SEL)aSelector client:(id)sender;
  处理由用户行为产生的命令,比如按下某个按键或点击了鼠标按键

解析文本数据

- (BOOL)inputText:(NSString *)string key:(NSInteger)keyCode modifiers:(NSUInteger)flags client:(id)sender;
  接收unicode、键盘码以及键盘的修饰标记数据

直接从文本服务管理器接收事件

- (BOOL)handleEvent:(NSEvent *)event client:(id)sender;
  处理键盘按下和鼠标事件

提交写作串

func commitComposition(Any!)
  通知控制器,提交组织串

获得输入串和候选串

func composedString(Any!)
  返回当前组织串

func originalString(Any!)
  返回组织前的unicode字串

func candidates(Any!)
  返回候选列表

常量

Info Dictionary Keys

IMKStateSetting

这是一个协议,它定义了设置和修改输入法状态的方法。下面是它的成员

激活和解除激活Server

func activateServer(Any!)
  激活输入法server

func deactivateServer(Any!)
  解除激活输入法server

显示偏好设置窗口

func showPreferences(Any!)
  显示偏好设置窗口

获得支持的事件

func recognizedEvents(Any!)
  返回表示事件掩码的无符号整形数

获得模式字典

func modes(Any!)
  返回与输入法关联的模式字典

存取值

func value(forTag: Int, client: Any!)
  返回与tag匹配的值

func setValue(Any!, forTag: Int, client: Any!)
  将值保存到key下

NSObject

这是Objective-C派生体系的根类,从该类可以继承Objective-C对象的基本能力。