宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
最初のリリース前の作り込みの話の続き。最終段階、ブラウズ領域の上下左右のポップアップボタンの実装について。このエントリは、XULにおける要素の重ね合わせ表示の方法に関する話が色々含まれています。多分。
実は当初は、こんな機能を付けるつもりは無かった。Split Browserのコンセプト自体が半分冗談みたいなものだったから、そこまで深く考えてなかったし。
ただ、実際に基本的な機能が揃って形になってくると、コンテキストメニューから「ブラウザを分割」を選んでさらにサブメニューから上下左右を選択する、という操作がだんだん鬱陶しくなってきた。と同時に、これをどうにかすることができれば使い勝手の面でブレイクスルーになるのではないか、という事も漠然と思うようになってきた。その最も安直な解決策として思い付いたのが、分割可能な領域の端にポインタが近付いたら勝手にボタンを表示して、それをクリックしたらその方向にそのブラウザを分割する、というものだったワケです。
ただ、思い付いたはいいものの、どうやって実装するかでだいぶあっちに行ったりこっちに行ったりウロウロしてた。
最初思い付いたのは、バインディングでの匿名内容の定義において水平・垂直それぞれのコンテナの両端に細長いボタンを付けておき、必要に応じて表示・非表示を切り替えるというものだった。
<content>
<xul:vbox>
<xul:button anonid="split-to-up"/>
<xul:vbox anonid="vContainer">
<xul:hbox>
<xul:button anonid="split-to-left"/>
<xul:hbox anonid="hContainer">
<xul:subbrowser/>
</xul:hbox>
<xul:button anonid="split-to-right"/>
</xul:hbox>
</xul:vbox>
<xul:button anonid="split-to-below"/>
</xul:vbox>
</content>
実際にこんな感じにバインディングの定義を変更して使い勝手を見てみたんだけど……正直、微妙っていうか、こりゃダメだという感想だった。
まず、この構造だと、ボタンが表示される瞬間にそれより内側の内容がガクッと動いてしまう。これを回避する手段として、タブのドラッグ&ドロップ時にドロップ位置を示す矢印が他の部分に重なって表示されるようになっているのと同じ手法を使って、ボタンをその内側のブラウズ領域に重ねて表示するようにしてみた。
Webページのレイアウトでは、ネガティブマージン(marginに負の値を設定すること)による要素の重ね合わせはよく見掛けテクニックだけど、XULでそれを試しても普通は全然効果が無い。実は、XULでネガティブマージンを機能させるにはちょっとしたコツが必要だ。
button[anonid="split-to-up"] {
position:relative;
height: 10px;
margin-bottom: -10px;
}
このように、position:relative
を指定し、ネガティブマージンの値を同じ幅または高さを設定することで、やっと、そのXUL要素が隣のXUL要素に重なって表示されるようになる。
しかし今回はこれでは全然役に立たなかった。通常、ネガティブマージンを指定された要素は隣の要素に重なって表示されるようになるんだけど、browser要素などのインラインフレームが隣り合うと、ネガティブマージンを指定された要素がフレームの内容の上に重なるのではなく、下に潜ってしまうんだ。
しかし仮にその点がクリアされたとしても、もっと根本的な所で使い勝手が悪いことにも気がついていた。よくよく考えれば分かることなんだけど、これだと、ブラウザを分割した時に「元からあったtabbrowser」と「新しく追加されたsubbrowser」との間ではなく、subbrowserよりも「向こう」の方にボタンが表示されてしまう。
<xul:button anonid="split-to-left"/>
<xul:hbox anonid="hContainer">
<xul:tabbrowser/>
<xul:subbrowser-container/> (追加されたsubbrowser)
</xul:hbox>
<xul:button anonid="split-to-right"/>
分割後のボックスの並びはこんな感じになる。ってことは、この例で「追加されたsubbrowser」と「tabbrowser」の間に新しくsubbrowserを追加するためには、「追加されたsubbrowser」よりも外側に表示されたボタンをクリックしないといけなくなるわけだ。これは全く直感的でない。
……というわけで、ここまでで書いた内容の実装は丸々ボツにして、別の方法を模索することにした。
次に思ったのは、自由に好きな位置に配置できる要素を使って、ポインタのある位置に動的にボタンを表示するというアプローチ。この場合、popup要素のようにそれ用の要素を使ってやる方法と、それ以外の普通のボックスを無理矢理(無理矢理という程でもないけど)ブラウズ領域の上に配置する方法の二通りが考えられる。
popup要素ノードは、showPopup()
メソッドを使うとスクリプト制御によって好きなタイミング・位置でそのポップアップを表示させることができる。Second Searchでもこの機能を使ってキー入力に応じてポップアップを表示させている。一応解説しておくと、以下のような感じ。
-1
を与えてやれば、第1引数の要素ノードの位置を基点にしてポップアップが表示される。この時、第5引数と第6引数でポップアップの位置を制御できる。
topleft
, topright
, bottomleft
, bottomright
のいずれかを文字列で渡す。それぞれ、第1引数の要素ノードのボックスの4つの角に対応している。topleft
, topright
, bottomleft
, bottomright
のいずれかを文字列で渡す。こちらは表示されるポップアップのボックスの4つの角に対応している。popup
, menupopup
, tooltip
のいずれかを文字列で渡す。どれを渡すかによってポップアップの挙動が微妙に変わるので注意。ちなみに、このメソッドはtooltip要素でもmenupopup要素でも使える。
しかし、これにもいくつか問題がある。
hidePopup()
メソッドで手動で閉じることもできるけど。実際、Tab Catalogでも当初はpopupを使って表示するつもりだったんだけど、上記のような問題に悩まされたので諦めてしまった。その時代わりに使ったのが、Split Browserでも採用していて、最後に紹介する、CSSのポジショニングを使ったやり方だった。
CSSのポジショニングでの方法を紹介する前にもう一つだけ他の方法も紹介しておくと、stack要素を使う方法というのもある。詳しくはリンク先を見ての通りなんだけど、stack要素の中に置いた要素ではleft
属性とtop
属性で好きな位置に要素を移動できるんだそうな。今回の目的を考えると、これが一番適した方法に思える。
ただ、この方法を採る場合は当然ながら、要素構造をいじらないといけない(ポップアップを表示したい範囲全体をstackの中に入れる必要がある)。CSSのポジショニングを使ったやり方には、stackを使ったやり方と同じような使い勝手で使えて、要素構造をいじらなくても済むという利点がある。
<vbox id="splitbrowser-add-button-container"
style="
position : fixed;
top : 0;
left : 0;
z-index : 32000;
">
<toolbarbutton id="splitbrowser-add-button"
oncommand="SplitBrowser.onAddButtonCommand(event);"
hidden="true"
style="
position : fixed;
z-index : 65000;
"/>
</vbox>
HTMLでポジショニングというとposition:absolute
を使う場合が多いと思うんだけど、XULの要素ではposition:fixed
を使う。これで要素がウィンドウ内で絶対配置されるようになるので、あとはtop
とleft
で好きな位置に移動する(スクリプトで制御する場合は要素ノードのstyle.left
とstyle.top
にアクセスすることになる)。なんでボタン自体にこのスタイル指定をせずにわざわざボックス要素を使っているのかというと、ぶっちゃけ、こうしないとちゃんと表示されなかったから。場合によっては別にこんな事しなくても大丈夫みたいなんで、そこはまあ、トライアンドエラーで試してみてねと。
なお、このvbox#splitbrowser-add-button-containerは、オーバーレイによってブラウザウィンドウの末尾に挿入されている。ポジショニングを使う場合は親要素がポジショニングで配置し直されていなければ別にどこに置いても大丈夫だ。
後はもう話は簡単で、mousemoveイベントをブラウズ領域の上で拾うようにして、内容領域の端の方に来たらボタンのhidden属性を外す、ただそれだけ。まあ、反応するエリアの大きさを上下左右辺の真ん中あたりに絞るとか(これは後のバージョンで加えた処理)、使い勝手のためにいくつか工夫はしてるけど。イベントハンドラは、要素のネストを考慮するのが面倒だったので、tabbrowser要素のコンテナになる要素とsubbrowser要素にバインディングで指定しておくことにした。
ここまででいちおう予定していた機能+アルファが全部揃ったので、バージョン0.1として公開した。ここまでで、作業時間は約2日。手の遅い自分にしてはなかなか頑張った方ではないだろうか。
とまあこんな感じの作業を最初のリリースまでの間にやっていた訳なんだけれども、このよもやま話はまだもうちょっとだけ続くんじゃよ(亀仙人)。
の末尾に2020年11月30日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2007-02-02_splitbrowser-popupbuttons.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。
writeback message: Ready to post a comment.