キー入力のオーバーライド

ここには、Text View に対してのキー入力のオーバーライドについてメモしていく。Table View でも同じようにオーバーライドはできた。

基本的には、NSTextView のサブクラスを作ってそこで keyDown をオーバーライドする。

こんな感じ。

include OSX

class InputTextView < NSTextView

def keyDown(event)

(ここに処理を入れる)

end

end

この event に入力されたキーの情報が入ってる。これは AppController のファイルに含めてもいいし、別のファイルを作ってもいい。AppController が短ければ入れたら楽かも。

まず、次のようなウィンドウを Interface Builder で作ってみた。英語の発音記号を入力する簡単なプログラム。ここでは母音だけ。普通にキーボードから入力できるものは除いてある。

AppController の outlet として、window と text をそれぞれ Window と TextView に結びつけた。

ボタンを押すと、入力できるようにもしたいので、次のようなメソッドを書いて、このアクションをすべてのボタンと結びつける。

def inputIPA(sender)

@text.insertText(sender.title)

end

ib_action :inputIPA

ついでに、フォントもちゃんと表示できるものにしたいので、awakeFromNib でフォントの設定をする。

def awakeFromNib

@text.setFont(NSFont.fontWithName_size("Lucida Grande",24.0))

end

さて、ここからが本番。NSTextView のサブクラスを作る。とりあえず、何が入力されたか知りたいので、文字と Key Code を見れるようにする。keyDown(event) メソッドをオーバーライドして、得られた NSEvent オブジェクトに対して characterskeyCode を使って文字や key code を得る。

class InputTextView < NSTextView

def keyDown(event)

p event.characters p event.keyCode end end

ここで Interface Builder に戻って、TextView をこのサブクラス(InputTextView)と結びつける。TextView を選んで Inspector の Identity で、Class から上で作った InputTextView を選ぶ。

今回のように Text View が一つしかないとかのときには関係ないんだけど、これを書いてるときに理解したのでここにメモ。(いずれはどこか別の項目に移すつもり)。

アプリケーションを立ち上げたときに、最初にフォーカスがあたっていてほしい View を指定したい場合は、その View を InitialFirstResponder にする。方法は、nib ファイルのその View がある WIndow を右クリックするか Window そのものを右クリックして、そこにある InitialFirstResponder を View に結びつける。

これでアプリケーションを立ち上げたときにこの TextView にフォーカスがあたっているはず。まあ、一つしかなければそれにあたるはずだけど。

さて戻って、これで保存する。試しに Xcode でビルドして実行すると、押したキーの文字と Key Code が表示される。a のキーを押した場合、次のような値が返る。

#<NSCFString "a">

0

この情報を使って、オーバーライドの設定をする。発音記号の入力には数字を使わないので、キーボードの数字の1から8に割り当ててみる。

ボタンに割り当ててある発音記号を使いたいので、サブクラス内でボタンを outlet をつくる。

ib_outlets :key01, :key02, :key03, :key04, :key05, :key06, :key07, :key08

これらをそれぞれのボタンに結びつける。

ここで、プログラムを実行して1から8までのキーの Key Code をメモっておく。次の通りなんだけど、なぜ順番じゃないんだろう?

18,19,20,21,23,22,26,28

これらの Key Code を得たときに、対応するボタンにアクセスしたいので、次のような Hash を作る。

@keyHash = Hash[*[18,@key01,19,@key02,20,@key03,21,@key04,23,@key05,22,@key06,26,@key07,28,@key08]]

これで、@keyHash[18](1のキー)が @key01 になる。これは、サブクラス内で awakeFromNib に入れておく。

ただし、これだけだと Shift、Option、Control キーなどを押していても、それぞれのキーに割り当てられている key code が変わるわけではないので、同じ key code が返ってくる。そこで、modifierFlags で一緒にどのキーが押されているかを捕まえる。

event.modiferFlags

いくつかのキーの組み合わせをここにメモしておく。

そして、次のようなスクリプトを書いてみた。

if @keyHash[event.keyCode] != nil && event.modifierFlags == 256

insertText(@keyHash[event.keyCode].title)

else

super_keyDown(event)

end

ここでしているのは、まず、@keyHash に得られた Key Code を入れてボタンが割り当てられていて、かつ修飾キーが押されていない場合は、ボタンの title(ボタンにある文字)を TextView に insertText で挿入する。何も割り当てられていなければ、nil が返るので、その場合は、super_keyDown(event) で入力された文字もしくはそれ以外のキー入力をそのまま処理する。修飾キーが押されている場合も同様。

追記:以前は、modifierFlags で得たキーコードを判別して使ったけど、実は、ちゃんと modifierFlags を扱う方法が用意されてた(まあ当然か)。

下に書いた定数を使って判別できる。例えば、コマンドキーを押しているかを判別したい場合は、NSCommandKeyMask を使って、次のように書く。(!= 0 か > 0 かとにかく、0 じゃない判定をする。)

if event.modifierFlags & NSCommandKeyMask != 0

処理

end

テンキーには、カーソルキーも含まれるようだ。

これで、すべて保存して実行すると、1から8のキーを押したときだけ、発音記号が入力される。ボタンをクリックしても入力できる。

最後に、このスクリプトの全容。このスクリプトをキャプチャーした時点では修飾キーのことを考慮に入れてなかったので入ってない

いずれは直すつもり。

require 'osx/cocoa'

include OSX

class AppController < NSObject

ib_outlets :window, :text

def inputText(sender)

@text.insertText(sender.title)

end

ib_action :inputText

def awakeFromNib

@text.setFont(NSFont.fontWithName_size("Lucida Grande",24.0))

end

end

class InputTextView < NSTextView

ib_outlets :key01, :key02, :key03, :key04, :key05, :key06, :key07, :key08

def keyDown(event)

if (event.modifierFlags & NSControlKeyMask) != 0 && @keyHash[event.keyCode] != nil

insertText(@keyHash[event.keyCode].title)

else

super_keyDown(event)

end

end

def awakeFromNib

@keyHash = Hash[*[18,@key01,19,@key02,20,@key03,21,@key04,23,@key05,22,@key06,26,@key07,28,@key08]]

end

end