Now browsing the archives for the 'General' category.

ロッカージェスチャの実装 Part 2

ロッカージェスチャの実装における問題点(ページロード遷移中の mouseup イベントが認識されない)への対策として、一度ロッカージェスチャを実行した後に少しでもマウスを動かすとロッカー状態を強制解除することに決めた。
つまり、マウスポインタを移動させないという制限付きで、左クリック(mousedown)→右クリック→右クリック→右クリック→・・・→左クリック(mouseup)での連続ロッカージェスチャを実行可能とする。

メリット

上記問題点が発生しても、ちょっとでもマウスを動かせば正常な状態に戻る。

デメリット

マウスを移動させながらの連続ロッカージェスチャが不可能。

TOP

All-in-One Gestures のマウストレイルを改造

All-in-One Gestures のマウスジェスチャ実装を改造し、マウスポインタを水平または垂直に移動させた場合、すぐ隣り合う座標へ新しく aioTrailDot 要素を配置するのではなく、現在の aioTrailDot 要素の大きさを縦や横に広げていくことで直線を描画するようにしてみた。下図の右が通常の実装方式、左が上記の修正を施したものである。わかりやすくするために各 aioTrailDot 要素へ outline: 1px solid blue で枠を付けている。

Mouse Trail

描く曲線にもよるが、これでジェスチャ中のメモリ消費量が削減できると思う。

TOP

All-in-One Gestures のマウストレイルで DOM Inspector が固まる問題

マウストレイルの実装 の中で、 All-in-One Gestures のマウストレイル実装方式の場合に DOM Inspector で Webページの DOM を調べながらマウスジェスチャをすると、Firefox がハングアップすると記したが、その原因はマウストレイル消去時に aioTrailContainer 要素へ appendChild された aioTrailDot 要素(マウストレイルの線を描画するための点)を removeChild せず、 aioTrailContainer 要素自体をいきなり removeChild しているためだとわかった。そこで、

while (elt.lastChild)
    elt.removeChild(elt.lastChild);
elt.parentNode.removeChild(elt);

みたいにして、まず aioTrailDot 要素を削除し、その後に aioTrailContainer 要素を削除するように修正したら DOM Inspector が固まる問題が発生しなくなった。

TOP

マウストレイルの実装

マウストレイルとは

マウスを右クリックして動かすと、その軌跡を表示する機能。右クリックを放すと軌跡は消滅する。

All-in-One Gestures の場合

DOM のレベルで実現している。右クリックの mousedown でジェスチャを開始すると、HTML の body 要素直下に aioTrailContainer 要素が生成される。その後、マウスを動かすにしたがってサイズが1×1の aioTrailDot 要素を複製して aioTrailContainer 下に appendChild する。この aioTrailDot 要素はサイズが1×1で、マウスポインタと同じ座標へ絶対配置されており、背景色がある。この aioTrailDot 要素を並べることで、あたかも一本のつながった線に見えるようになる。
この方式には以下のような問題点がある。
・縦長のページでマウストレイルを行うと重たい
・曲線が滑らかに描画されない
・DOM Inspector で Webページの DOM を調べながらマウスジェスチャをすると、Firefox がハングアップする。

Optimoz Mouse Gestures の場合

Windows では、C++製?の独自 XPCOM コンポーネント (mgMouseService.dll) を用い、 Windows ネイティブな?実装によってマウストレイルの描画を実現している。
この XPCOM は mgIMouseService というインタフェースを有し、 initTrails と stopTrails の2つのメソッドによって XUL からマウストレイルの描画を制御することができる。

Windows 以外のプラットフォームでは All-in-One Gestures と同じ実装方式である。

他にうまい方法はあるか?

マウストレイル開始時に ブラウザ上に canvas 要素をかぶせ、 dot の stroke を連続で行うことで実現可能。この方法では、 All-in-One Gestures の実装方式の問題点が解決され、曲線が滑らかに描画でき、縦長のページで動作が重たくなることも無い。しかし、以下のような問題点がある。
・canvas 要素が邪魔をしてマウスが通過したリンクを調べることができない。
・一時的にメモリ使用量が激増する。

TOP

ロッカージェスチャの実装 Part 1

[userChrome.js] 軽量マウスジェスチャーでの問題点

ロッカージェスチャを使って「戻る」をして、ページロード中に右クリックを放すとそれが認識されない(mouseup イベントが発生しない)。その後通常の左クリックをしただけでロッカージェスチャが実行されてしまう。

All-in-One Gestures の場合

上記とまったく同様のバグが発生することがわかった。ただ、AiOG ではロッカージェスチャ実行後にタイマーが仕掛けられ、3秒間何もしないとロッカージェスチャの待ち状態が解除される(つまり右クリックを放したと見なされる)という仕組みがある。もしかするとこのタイマーはバグが発生したときの被害を最小限に食い止めるための意味があるのかも?つまり、右クリック放しが認識されずにバグ状態に陥っても、3秒間我慢すれば勝手に正常な状態へと戻る。

Optimoz Mouse Gestures の場合

Windows の場合はバグ発生しない。 Linux の場合はバグ発生する。
なぜ Windows の場合にバグが発生しない(mouseup イベントが必ず発生する)かを調べたところ、 OMG では独自に実装したC++製?のXPCOMコンポーネントによって、 DOM のレイヤーで発生する mouseup イベントとは別にもっと上のレイヤー?でマウスの動きを検知しているためである。この XPCOM コンポーネントは mgIMouseService というインタフェースを有し、マウストレイル(マウスジェスチャ中の軌跡描画)が主な仕事であるが、それ以外にもマウスの動きやクリックに応じて nsIObserverService による通知を行う仕組みをもっている。 XUL 側ではこの通知を監視し、クリックの放し (mozgestButtonUp) が発生すると initMouseEvent によって DOM のレイヤーでの mouseup イベントを生成する処理になっている。

結論

マウスジェスチャとホイールジェスチャは mousedown や mousemove といった DOM イベントを捕捉することで実装できた。しかし、まともなロッカージェスチャを DOM のレイヤーのみで実装するのは無理っぽい。

TOP

Firefox Developers Conference Summer 2007

昨日開催された Devcon 2007 のプレゼン資料をアップしました。その他にも、過去に使用したプレゼン資料も整理してまとめてアップしました。
» プレゼン資料のページ

なお、PowerPoint ファイルから PDF への変換は Drawloop を使用しました。登録さえすれば無料で Word / Excel / PowerPoint から PDF への変換が可能となる Webサービスで、 Ajax な UI もシンプルで使いやすい。以前は日本語フォント未対応だったが、今はまったく問題なし。大変ありがたいです。

TOP

マウスジェスチャについてのアンケート結果

マウスジェスチャについてのアンケートにご協力ありがとうございました。

Q1. あなたはマウスジェスチャの拡張機能を使っていますか?

All-in-One Gestures を使っている 77
Optimoz Mouse Gestures を使っている 9
userChrome.js 用マウスジェスチャを使っている 99
使っていない 14

Q2. マウスジェスチャ機能は必要ですか?

はい 182
いいえ 8
わからない 7

Q3. ロッカージェスチャ機能(右クリックしながら左クリック)は必要ですか?

はい 56
いいえ 116
わからない 27

Q4. ホイールジェスチャ機能(右クリックしながらマウスホイール)は必要ですか?

はい 87
いいえ 93
わからない 20

Q5. ミドルクリックでのホイールジェスチャ機能(ミドルクリックしながらマウスホイール)は必要ですか?

はい 12
いいえ 158
わからない 30

Q6. タブバー上でのマウスホイールによってタブを切り替える機能は必要ですか?

はい 85
いいえ 98
わからない 17

Q7. マウスジェスチャ中の軌跡描画(マウストレイル)は必要ですか?

はい 36
いいえ 154
わからない 10

Q8. マウスジェスチャ中のステータスバー表示は必要ですか?

はい。「LR」のように、現在の方向を表示すべき。 31
はい。「LR (タブを開く)」のように、現在の方向と機能名称を表示すべき。 123
いいえ 41
わからない 5

Q9. ジェスチャの方向の表現方法はどれが最適ですか?

L、R、U、D 71
左、右、上、下 7
←、→、↑、↓ 111
わからない 11

Q10. 斜め方向のジェスチャの認識は必要ですか?

はい 18
いいえ 163
わからない 18

Q11. ジェスチャのタイムアウト(ジェスチャ中に数秒間じっとしているとジェスチャの認識を停止する機能)は必要ですか?

はい 114
いいえ 55
わからない 29

Q12. ジェスチャ中に通過したすべてのリンクをタブで開く機能は必要ですか?

はい 41
いいえ 120
わからない 38

Q13. 「タブを閉じる」機能に最適なジェスチャの割り当ては?

↓→ 68
46
↓↑ 10

Q14. 「新しいタブを開く」機能に最適なジェスチャの割り当ては?

25
19
←→ 7

Q15. 「閉じたタブを元に戻す」機能に最適なジェスチャの割り当ては?

↓← 20
↓↑ 18
↑↓ 11

Q16. マウスジェスチャに求めるものは?

動作の軽さ 168
機能の豊富さ 32
設定のしやすさ 100
ユーザスクリプトへの対応 52

TOP

ブックマークツリーの右クリックメニュー実装方式

Trunkへ正式に搭載されたことだし、そろそろPlacesの仕組みとかを勉強していこうかと思ったが、まず最初に気になるツリーの右クリックメニューの実装方式を見て一安心した。 chrome://browser/content/places/placesOverlay.xul を見ればわかるとおり、右クリックメニューが普通の popup 要素と menuitem 要素の集まりで実装されているのだ。これなら拡張機能が右クリックメニューへ独自のメニューを追加したければ、通常の XUL のオーバーレイで実現できる。

というのも、 Firefox 2 でのブックマークツリーの右クリックメニューは、右クリックでしたときの popupshowing イベント発生時に BookmarksCommand.createContextMenu という JavaScript 関数がすべての menuitem 要素を document.createElementNS で動的に生成してメニューを表示するという実装方式なのだ。これが大変厄介で、拡張機能から右クリックメニューへ新たなメニューを追加しようとしても、以下のような醜いやり方をせざるを得ない(もしかすると、別のもっと美しいやり方もあるかも?)。

方法1 BookmarksCommand.createContextMenu を、本来の関数を内包する独自の関数へ置き換える

これは Locate in Bookmark Folders という拡張機能で見かけたやり方だが、 menuitem 要素を動的に生成している関数を包含した独自の関数へ置き換えてしまい、ひとつの popupshowing イベントハンドラ内で Firefox 本来のメニューと拡張機能独自のメニューの両方を動的に生成する方法だ。

BookmarksCommand.originalCreateContextMenu = BookmarksCommand.createContextMenu;
BookmarksCommand.createContextMenu = function(aEvent, aSelection, aDS) {
  // まずはじめにオリジナルの関数を呼び出してから...
  this.originalCreateContextMenu(aEvent, aSelection, aDS);
  // document.createElement で menuitem 要素を生成し、 menupopup 要素 (aEvent.target) へ appendChild する。
  // 右クリックメニューの末尾ではなく特定位置へメニューを追加したければ insertBefore する。
};

方法2 右クリックメニューに対する popupshowing イベントを追加し、 JavaScript によって menuitem 要素をさらに追加する

まず、 chrome://browser/content/bookmarks/bookmarksTree.xml#bookmarks-tree-name を見るとわかるように、ブックマークツリーの右クリックメニューのポップアップ (menupopup 要素) は XBL で定義された bookmarks-tree 要素内の匿名要素であり、イベントハンドラ追加のために要素へアクセスするには、

var bmtree = document.getElementById("bookmarks-view");
var bmpopup = document.getAnonymousNodes(bmtree)[1];
// ちなみに[0]は[Object comment]

あるいは、

var bmtree = document.getElementById("bookmarks-view");
var bmpopup = document.getAnonymousElementByAttribute(bmtree, "onclick", "event.stopPropagation();");

なんていうややトリッキーなやり方をするしかないようだ。こうして得られたポップアップに対して、イベントハンドラを追加する。

function injectMenuItems(event) {
  // document.createElement で menuitem 要素を生成し、 menupopup 要素 (event.target) へ appendChild する。
  // 右クリックメニューの末尾ではなく特定位置へメニューを追加したければ insertBefore する。
}
bmpopup.addEventListener("popupshowing", injectMenuItems, false);

TOP

Bug 339546 – nsIFile.remove should allow option to send to Recycle Bin/Trash

現状、ファイルやディレクトリを削除するために nsIFile.remove をやるとディスクから完全削除されてしまう。ごみ箱へ送ることもできるようになるよう、期待したい。

Bug 339546 – nsIFile.remove should allow option to send to Recycle Bin/Trash

TOP

Gran Paradiso Alpha 4 で FUEL 使用不可

使い方がよくわからないので色々試してみたけど時間の無駄だった。
Bug 379139 – FUEL 0.1: Component/typelibs need to be added to installer manifests

TOP