たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。
以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
Split Browserでも使いまくってるsplitterの注意点。
Mozilla Suiteの頃にはsplitterの中央付近にgrippy(つまみ)が表示されて、それをクリックしたら左右(上下)のどちらかに要素を折りたたむという挙動を示すんだけど、Firefoxの標準テーマではgrippyがdisplay:none
されてるのでぶっちゃけ使えない。拡張機能側のテーマでgrippyの外観を定義するか、grippyに外観が定義されてるテーマを使うかのどっちかをしないと、grippy要素を追加しただけでは何も起こらない。
grippyをクリックした時に前後に折りたためるようにするcollapse
属性というのがあるけど、そういう訳なので、実際にはこの機能はFirefoxではほとんど役に立たない。でも、この属性があるかないかでスプリッタの挙動が若干変わるのには注意が必要だ。
例えばこんな場合を想像する。
<vbox>
<hbox id="above" flex="1"/>
<splitter/>
<vbox id="below" flex="1">
<toolbar/>
<hbox id="content" flex="1">
</vbox>
</vbox>
こういう構造の時、スプリッタをドラッグすると、スプリッタの前後にある要素の大きさが無段階で伸縮する。しかし下側のボックスの中にはflex属性が設定されていないtoolbar要素があるので、スプリッタをドラッグしてvbox#belowの大きさを変化させても、実際に伸縮するのはhbox#content(とvbox#below)とhbox#aboveだけになる。結果として、vbox#belowの大きさはtoolbar要素の高さより小さくはならない。toolbarより高さを小さくしようとしても、そこでつっかえてそれ以上スプリッタが動かなくなる。
<vbox>
<hbox id="above" flex="1"/>
<splitter collapse="after"/>
<vbox id="below" flex="1">
<toolbar/>
<hbox id="content" flex="1">
</vbox>
</vbox>
でもこんな風にcollapse属性を指定すると、toolbar要素にflex属性が無くても、つっかえずにスプリッタをボックスの端までドラッグできるようになる。そして端まで達すると下のボックスは折りたたまれて非表示になる。
言い換えれば、スプリッタを動かしても折りたたまれず常に表示されたままになるようなツールバーとかを実装したければ、collapse属性を使わず、常に表示しておきたい要素にflex属性を設定しない、という事でそれを実現できるわけです。
という、小ネタ。
Split Browserの作り込みの話のおまけ。このエントリにはドラッグ&ドロップの実装に関する話が含まれているかもしれません。
まず基本の話として、Firefoxで(というかXULで)ドラッグ&ドロップを実装するには、ondraggesutre, ondragover, ondragenter, ondragexit, ondragdropの5つのイベントハンドラと、XPCOMの機能を使う必要がある。このあたりの話はMDCのXULチュートリアルには無いんだけど、古いXULチュートリアルには載ってるので、熟読しとくことをお勧めしたい。
XUL要素をドラッグしようとすると、draggesutreというイベントが発行される。いわゆるAjaxとかだと、ボタンを押下→マウスが動いた、という操作をそれぞれ別のイベントで拾わないといけなかったり、クリック時にマウスがブレただけでドラッグ開始と判断してしまわないように閾値を設定したり、と色々めんどくさい配慮がいるんだけど、XULではdraggestureイベントいっこ拾うだけで済むので話が早い。
ドラッグ中にボタンを放した時、つまりドロップの操作が行われた時には、dragdropというイベントが発生する。これは他のアプリケーションからのドラッグ&ドロップでも発生するので、アプリケーションの垣根を越えてのデータのやりとりもできる。やろうと思えば多分バイナリデータも渡せるんじゃないかな……やったことは無いけど。
あとの3つのイベントはおまけのようなもので、ドラッグ中にポインタが載った要素に対して、今ドラッグ中のデータをドロップできるかどうか(その要素がそのデータのドロップを受け入れられるかどうか)を示す、とかそういった用途で使うことが多い。
データの受け渡しにはXPCOMの機能を使う。詳細は旧チュートリアルの当該項目で解説されてる……ンだけど、ぶっちゃけこんなの真面目に使ったらあかん(ぉぃ)。これをラッピングして簡単に使えるようにしてくれる物として、nsDragAndDropという標準ライブラリがあって、これはFirefoxでも利用できる(っていうかFirefox内部で使われまくり)ので是非活用しましょう。旧チュートリアルのnsDragAndDropの使い方の解説と利用例は要チェックですよ。
……というのがドラッグ&ドロップの実装の基本。ここから先は、その応用。
最初のリリース前の作り込みの話の続き。最終段階、ブラウズ領域の上下左右のポップアップボタンの実装について。このエントリは、XULにおける要素の重ね合わせ表示の方法に関する話が色々含まれています。多分。
実は当初は、こんな機能を付けるつもりは無かった。Split Browserのコンセプト自体が半分冗談みたいなものだったから、そこまで深く考えてなかったし。
ただ、実際に基本的な機能が揃って形になってくると、コンテキストメニューから「ブラウザを分割」を選んでさらにサブメニューから上下左右を選択する、という操作がだんだん鬱陶しくなってきた。と同時に、これをどうにかすることができれば使い勝手の面でブレイクスルーになるのではないか、という事も漠然と思うようになってきた。その最も安直な解決策として思い付いたのが、分割可能な領域の端にポインタが近付いたら勝手にボタンを表示して、それをクリックしたらその方向にそのブラウザを分割する、というものだったワケです。
ただ、思い付いたはいいものの、どうやって実装するかでだいぶあっちに行ったりこっちに行ったりウロウロしてた。
作り込みのフェーズの続きですよ。だいぶ間が空いてしまったけど。
ブラウザの分割の状態を保存する、と一口に言っても、保存するべき内容は2つのレイヤに分けることができる。
1の情報の保存と復元の処理は、nsISHistoryの状態の保存と復元が鍵になる。これはFirefox 2のセッション保存機能を実現しているnsSessionStore.jsの中身を見れば理解できるだろう。要約すると、処理の流れは以下のようになる。
sessionHistory
プロパティ(nsISHistoryのインスタンス)のgetEntryAtIndex()
メソッドで、個々のヒストリエントリ(「戻る」「進む」で辿れる個々の履歴)を取得する。sessionHistory
プロパティにnsISHistoryInternalインターフェースでアクセスして、addEntry()
メソッドでヒストリエントリとして追加する。tabbrowser要素に対しては、これをタブの数だけ繰り返せばいい。
1日目でとりあえず必要最低限の機能は揃ったので、2日目は作り込みのフェーズに入った。
最初の方で作った分割後の領域用のsubbrowserウィジェットは、ブラウザとしてまともな機能が全然無い状態だった。「戻る」や「進む」などのボタンは一応あったけど履歴が何もないときでも常時押せる状態だったし、faviconもページのタイトルも何も表示されないような物。これではさすがに話にならない。
subbrowserの中に何を入れるかなんだけど、今後のことはともかくとして、最初に公開するバージョンでは、「戻る」「進む」「更新」「中止」の4つのボタンと「ロケーションバー」+「移動」ボタン、「Webサイトのアイコン(favicon)」、読み込み状況を示す「プログレスバー」、それから前のエントリでもチラッと出てきたけど「閉じる」ボタン。これだけ設けておくことにした。
ただ、これだけの要素をあの狭いスペースにそのまま置くとだいぶアレなことになるので、Safariを参考に、「更新」と「中止」のボタン、それから「プログレスバー」と「ロケーションバー」はそれぞれ一つにまとめたんだけど。前者はCSSでの指定、後者はXULのスタックによって実現できた。
いつまで続くんだこの話。やっと開発一日目最後のところだよ。コード書くのは手っ取り早いけど文章書くのってだりい。ドキュメント整備が遅れるのは世の常ですね。
ええと。とりあえず「分割」する、ブラウザをどんどん増やす方向については前回までのところでできたんで、次は追加したブラウザを削除する方向の処理の話です。
例として、「一回右に分割して、さらにその分割されたブラウザを下に分割する」という場合を想定してみよう。
これはDOM操作的には、hboxにsubbrowser-containerを入れて、さらにその中のvboxにsubbrowser-containerを入れる、という感じになる。タグで書いたらこんな感じだ。
<subbrowser-container>
<vbox>
<hbox>
<box anonid="wrapper">
<tabbrowser/> <!-- 左 -->
</box>
<splitter/>
<subbrowser-container>
<vbox>
<hbox>
<box anonid="wrapper">
<subbrowser/> <!-- 右上 -->
</box>
</hbox>
<splitter/>
<subbrowser-container>
<vbox>
<hbox>
<box anonid="wrapper">
<subbrowser/> <!-- 右下 -->
</box>
</hbox>
</vbox>
</subbrowser-container>
</vbox>
</subbrowser-container>
</hbox>
</vbox>
</subbrowser-container>
右下のブラウザを削除する場合はどうするか? これは分かりやすい。subbrowserの親の親の親の親にあたるsubbrowser-container要素を削除すればOKだ。また、左の部分、つまり最初からFirefoxにあるブラウザは削除できないので、ここも問題無いといえば問題無い。
難しいのは、右上の位置にあるブラウザ……何らかの子ブラウザを抱えている、中間の階層のブラウザを削除する場合だ。
前のエントリではさらっと最終的な設計だけ示したけど、これは実は、この後の段階の作業で分かったことやなんかをフィードバックした結果たどり着いた、(少なくとも自分的には)一番洗練された状態だ。ここに至るまでにはだいぶ無駄に寄り道してた。
Split Browserの話の続き。間が空いてしまったけど。
そもそも何故「分割」なのか? いわゆるMDIアプリケーションのように普通にブラウザをたくさん置けないのか? という疑問は結構多くの人が思うものだと思う。
これはXULの仕様上の制約による部分が大きい。XULの場合は基本的に「内容が横に並ぶボックス(hbox:horizontal box)」と「内容が縦に並ぶボックス(vbox:vertical box)」という縦横の箱の集まりとしてGUIの構造を定義するようになっていて、要素の重ね合わせというのはイレギュラーな事態だからだ。
まあ、この制限を乗り越えてボックスを好きな位置に表示するテクニックというのはいくつかあって、タブのドラッグ&ドロップの時に出てくる矢印や、フィッシング詐欺警告とかのように、Firefox本体でもそんなテクニックが使われていたりはする。しかし現状では、タブの切り替えやbrowser要素等と重なったときに色々と予期しない結果になってしまうこともあって、あくまで補助的にしか使えないかなーというのが僕の印象です。Split Browserでもこのテクニックを使っている部分があるけど、これはまた後述します。
あと、さらに余談だけど、Firefox 3だか4だかではSVGのサポートが改善されてSVGのforeignObject要素の中にインラインフレームを置けるようになるそうで、これを使うともっと自由にブラウザを表示できるようになるのかもしれない。
前のエントリの続き。
「ブラウザを分割する」という機能を考えた時、構成要素として、一通りの機能を持ったブラウザのサブセット的なパーツは絶対に必要になる。
幸い、XULにはbrowser要素というインラインフレームの一種があって、このタグをいっこ書くだけで「戻る」「進む」などの機能を含めたWebブラウザの基本的な機能を持ったブラウザを簡単に作れるようになっている。
var browser = document.createElement('browser');
document.documentElement.appendChild(browser);
browser.goBack();
browser.loadURI('http://piro.sakura.ne.jp/');
ただ、実際に使うにあたっては、そういった機能を呼び出すためのツールバーボタンやメニューなどは自前でちまちま用意しないといけない。
コンテントホルダーでは、メインのブラウズ領域とコンテントホルダーの提供するブラウザとの間の仕切り(splitter要素)そのものをツールバー代わりにして、そこにボタンを配置してみた。ただ、このやり方にはいくつか問題がある。
Split Browserを作るに至った経緯とかその辺の話。拡張機能勉強会なんてのも開催されるので、まあ、もしかしたら誰かの何かの役に立つかもなあ、と。のべ時間にして24時間くらいでできたんで、割と細かいところまで覚えてるからというのも理由の一つではあるんだけど。