Bugzilla等でリストボックスをホイールでスクロールしていると、そのスクロールが限界になった途端、祖先(例えばページ全体)がスクロールされるのは良くないというバグ。なんとか、これでどうだろうか、という仕様で修正を入れてみた。
こういうバグはユーザの感覚に違和感を与えずに配慮ある挙動をしなければいけないので非常に難しい。実際、作業に入ってから既に二ヶ月ぐらいかかってしまった。
まず、現状の動作に何故違和感があるのかを考えてみた。Matsの言う、hierarchical scrolling
に問題があるのは間違いない。これは、ホイールが回された要素から順に祖先に辿っていき、最初に見つけたスクロール可能な要素でスクロールを行うというもの。つまり、ホイールを回す度にスクロールする要素を探すことになる訳だが、ユーザはスクロールしたい要素を一度だけ決め、連続してホイールを回すわけだから、ここに齟齬があることが分かる。そこで、複数のマウスホイールのイベントを一つのトランザクションとしてまとめてしまい、そのトランザクション内ではひとつの要素でのみスクロールイベントを処理することでこれを解決できるはずである。
では、どのような基準で複数のイベントをひとつのトランザクションとするか、という点が問題になる。まとめすぎるとスクロールしたい要素がスクロールしなかったり、まとめ方が不十分だと、このバグのように不満が出る。
まず最初にトランザクションの切れ目として考えたのは各マウス、キーボードイベントである。マウスやキーボードを操作するということは、スクロール以外のことをユーザが行ったということなので、間違いなくその前後でトランザクションは異なるはずである。しかし、ホイールを回すとき、マウスが移動してしまって、そのイベントで分断されてしまっては元も子もない。そこで、デフォルトでは0.5秒間はマウスの移動を無視するようにしている(マウスの移動イベント自体はそのまま処理される)。これはmousewheel.transaction.ignoremovedelay
で調整可能である。かなりテストした結果、この時間で長すぎず、短すぎず、丁度良いと思う。ただし、この時間内であっても、カーソルがスクロールターゲットの要素の外に移動した場合はトランザクションを終了するようにしている。これは、違和感を無くすためである。
そして最後まで悩み続けたのが(今も良かったかどうかは懐疑的)なのが、タイムアップによるトランザクションの終了である。例えば、マウスを動かさないままでホイールを回して特定の要素をスクロールしたとする。そして、一時間放置して、マウスを移動させないままホイールを回したとする。この場合、前回スクロールした要素ではなく、従来通り、カーソルの下にある要素がスクロールされるべきだろう。これはマウスでは非現実的だが、ノートのタッチパッドなんかでは起こりやすいかもしれない。そこでこのタイムアウトを3秒にしてみた。テキストを読みながらカーソル位置を確認せずにホイールを回し続けた場合、この時間では不足かもしれない。そこで最初は120秒に設定してテストしていたのだが、原理を知っている私でも混乱することがしばしばあった。そこで、最終的には3秒にまで縮めて、とにかく違和感、混乱を与えないようにしつつ、できる限りトランザクションを維持するようにしてみた。しかし人によってはまだこれでも長いと感じる人も居るかもしれない。その意見があるようなら、さらに時間の短縮を試みる必要があるだろう。こればっかりはNightlyでより多くの人にテストしてもらわないとどうしようもない。ちなみにこの時間はmousewheel.transaction.timeout
で修正できる。
これらの設定を共にゼロに設定すると従来と同じ動作になるが、新しい挙動になれると、やはり従来の挙動は不便に感じると思う。ただ、新しい挙動がベストである自信はいまだに無い。