Oct 11, 2008

ルーラーバーで折り返された行のカーソル位置を正しく表示できるようになったよ

ルーラバーだとやっぱり行ったことのある場所に行くあの呪文しか思い浮かばないという声があったので、日本語名をルーラーバーに変えた。

そのルーラーバーなんだけど、Rulerrrrrでもあった「折り返された次の行の先頭にカーソルがある時や行末にカーソルがある時にルーラー上の現在位置表示がおかしくなる(カーソル位置の計算に失敗する)問題」に真面目に取り組んで、0.2.2008101101でだいぶ改善した。

背景

ルーラーバーもRulerrrrrも、現在のカーソルの位置を計算するのには選択範囲を使っている。FirefoxやThunderbirdではカーソル位置をJavaScriptから直接取得することはできないんだけど、現在カーソルがある位置は長さ0の選択範囲として取得できるので、選択範囲が含まれているノードとか選択範囲の開始・終了位置などからどうにかこうにかして「カーソルより前に何文字あるか」を数えて、カーソル位置を割り出している。

行末にカーソルがある時

行末にカーソルがある時にカーソル位置が0(行頭)と判定されてしまっていたのは、この選択範囲から選択範囲が含まれているノードを取得できないせい。どういう事かというと、行末にカーソルがある状態というのは「テキストノード」「改行のBR要素」「テキストノード」という順番にノードが並んでいてBR要素の直前に長さ0のRangeがあるという状態で、「選択範囲が含まれているノード」は前後の要素やテキストノードではなくいきなりBODY要素になってしまう、ということでカーソル位置の求めようがなくなっていた。

そこで、TreeWalkerを使って各行のテキストノードやBR要素を走査し、compareBoundaryPoints()でそのノードがカーソルに隣接しているかどうかを調べる、という風な処理を入れてみた。これにより、行末にカーソルがある時でもカーソルより前にある文字を数えられるようになった。

折り返された行の行頭・行末判別

折り返された行の処理はもうちょっと厄介だった。普通に考えたら、「カーソル位置より前の文字数÷折り返し文字数 の余り」でカーソルの現在位置が分かるはずなんだけど、実際にはこれだけじゃダメだった。FirefoxやThunderbirdのエディタ機能では、折り返された行の行末にカーソルがある時に右キーを押すと、次の文字(折り返された次の行の先頭文字)の後の位置にカーソルが移動するのではなく、次の文字の前・仮想的な改行文字の後にカーソルが移動してしまう。折り返された後の行頭で左キーを押した時も同様。なので、いくら文字数ベースで計算しても、今「折り返された行の折り返し直前にカーソルがある」のか「折り返された後の行の先頭にカーソルがある」のかは分からない。

幸い、選択範囲の変更(=カーソルの移動)を監視する時にはその選択範囲の変更が発生したユーザの操作の種類がある程度分かる。なので、マウス操作でのカーソル移動については、行の左の方でのクリックでカーソルが移動した時は行頭、そうでなければ行末と判定するようにした。また、キー操作でのカーソル移動については、「直前にいた位置が行頭・行の中程・行末のどれだったか」と「どのキーが押されたか」の組み合わせを元に、今カーソルがどの位置にあるかを推測するようにした。

英単語等があるせいで予定の文字数より前で折り返しが発生した時や、HTMLでプロポーショナルフォントが使用されている時、画像がある時などについてはもう完全にお手上げです。カーソルの位置をピクセル単位で取れるようなAPIが付いてくれないことには、もうどうにもなりません。

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能