May 03, 2007

ドラッグ&ドロップ中のタイマーを擬似的に再現する

Windowsのエクスプローラでは、ファイルなどのドラッグ中にフォルダの上でしばらく待つと、そのフォルダを勝手に開いてくれる。Firefoxのブックマークのフォルダもそういう風になっている。これと同じように、Second Searchの挙動を変えてみよう、と思って色々実験してみた。

目指す挙動は、「検索バーの上に文字列をドラッグしてしばらく待つと、ポップアップが出てきて検索エンジンを選択できる」というもの。

まず最初に思いつくのが、JavaScriptのタイマーを使ってやる方法。でも、Split Browserの実装の解説でも触れたけど、Firefoxではドラッグ&ドロップの最中はJavaScriptのタイマーは(window.setTimeout()window.setInterval()も)一切停止してしまって、ドロップ後にまとめて動き出すようになっている。

もしかしたらXPCOMの方のタイマーなら……と思ってnsITimerを使ってみたけど、これも同じだった。どのタイプのタイマーを設定しても、JavaScriptのタイマーと同じ現象が起こってしまった。

ていうかそもそもの話、「ブックマーク」メニューやブックマークツールバーにおいては、すでにこのような挙動が実現されてるわけで。だったら最初っからそこら辺のソースコードを見るのが早いよね、と。

そういうわけでメニューの定義とかあちこち見ていたら、このあたりの挙動はBookmarksMenuDNDObserverというオブジェクトが受け持っていることが分かった。各メソッドの中身を見ていくと、onDragEnterSetTimerとかonDragExitSetTimerとかいう名前のメソッドがあることに気がついたので、何となく怪しいのでそっちも見てみたら、ようやく謎が解けた。

まず冒頭に書いた、ドラッグ中にすべてのタイマーが止まってしまう問題なんだけど、コードを見てみるとこれはどうやらWindows環境のみの制限らしい。後で試してみたら、確かにLinuxではドラッグ中でもタイマーが動いてくれた。Firefoxのブックマークまわりでも、Windows環境以外では(実質的には、LinuxとかのUnix系だけみたいなんだけど)普通にタイマーを使ってこの挙動を実現しているようだ。

じゃあWindowsではどうしてるのかというと、代わりにdragoverイベントを使っていた。

WindowsでもLinuxでも、オブジェクトをドラッグしていて何らかのXUL要素の上にポインタがある時は、mouseoverイベントの代わりにdragoverが発生する。このdragoverイベントの面白いところは、まるでキーリピートがかかっている時のkeypressイベントのように、ポインタが要素の上にある間は一定の時間間隔で連続してイベントが発生し続けるという点だ(mouseoverもそうだったっけ?)。

そこでFirefoxのコードでは、こういう事を行っている。

  1. まず、dragenterdragexitのイベントが発生した時点で、その時刻をどこかに保持しておく。
  2. dragoverイベントをハンドリングして、ハンドリングした時点での時刻と、dragenterdragexitのイベントが発生した時刻とを比較する。
  3. この時、一定の時間(ブックマークまわりの場合は350ミリ秒でハードコーディングされてる)が経過していなければ何も行わない。一定以上の時間が経過していれば、ポップアップメニューを開く。

あんまりスマートではないけど、現状ではこれ以外に方法は無いようだ。というわけでさっそくSecond Searchでも同じ処理を行うようにしてみたところ、無事Windowsでも望み通りの動作を行えるようになった。めでたしめでたし。

ちなみに、Mac OS上のFirefoxではこの処理が全面的に無効にされてしまっている。検証してないけど、これは、どうがんばっても無理って事なんすかね。

このWindows環境特有の問題はFirefox 3では解消されたようです。

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能