Last update 1999/10/07

論理レイアウト

(C)平山直之
無断転載は禁止、リンクはフリー
誤字脱字の指摘は歓迎


リソースエディタの場合

VC++でダイアログを編集するとしますよね。

その場合、統合環境のダイアログエディタを使います。実際の編集作業は、

LLayout1.gif (4136 バイト)

こんな感じのダイアログエディタで行うわけです。

VC++のダイアログエディタでは、ダイアログに置かれるオブジェクトには階層構造はなく、オブジェクトの位置は絶対座標(といってもクライアント領域の左上を基本とする)で指定します。

通常、大きさの変化しないダイアログであれば、これで問題になることはあまりありません。問題になるのは、大きさが変化するダイアログを作りたい場合です。

VC++の方式では、オブジェクトの座標やサイズを絶対座標で指定します。したがって、たとえば上のダイアログでダイアログの大きさの変化に伴ってOKボタンやキャンセルボタンの右マージンを一定に保ちたい場合、WM_SIZEに反応してボタンの場所を移動するようなコードを記述しなければいけません。

われわれが扱っているものは所詮コンピュータです。ですから、たとえあなたのイメージでは「右マージンを一定に保ちたい」のような抽象的なデザインを持っていたとしても、出力先がなんであれ最終的には絶対座標に変換しなければなりません。それが直接フォームデザイナに露出しているのが伝統的なリソースエディタのダイアログエディタであるといっていいでしょう。

以後、このように絶対座標を直接取り扱うデータを「物理レイアウト」とします。

Delphiの場合

一方、Delphiのフォームエディタは、VC++のものと比較すると、見た目では大差ないものの、レイアウト能力では1歩進んでいます。細かい違いはそれこそ山のようにありますが、その中でも大きなものを挙げるなら、以下の2点になるでしょう。

前者はいじれば誰でもわかるので省略するとして、ここで話題にしたいのは後者のAlignの概念です。

例えば、Win95のインターネットエクスプローラのようなフォームを作りたい、としましょう。インターネットエクスプローラではなくて、普通のエクスプローラです。

エクスプローラのフォームの要点は以下の2つです。

サイズ変更が可能ですから、VC++で実現するなら、WM_SIZEに反応して中に入っているListViewとTreeViewのサイズ・場所を変更するようなコードを書かなければなりません。

が、Delphiでは、エクスプローラのようなフォームを作るのにコードを書く必要がありません。

だけで用が済んでしまうからです。Splitterを加えるとしても、TreeViewとListViewの間にalLeftで挟むだけです。

Alignプロパティは、おおざっぱに言うと、「親のサイズに合わせてオノレのサイズ・位置を変えるかどうか、また変える場合はその方法」を指定するプロパティです。上の例で言うと、alLeftはコンポーネントの幅は変更しないで高さと位置を親コンポーネントの左側にべったりはりつくようにするものであり、alClientは高さ・幅・位置のすべてを親とべったり一緒にするものです。ただしそのときにalClientが指定されたコンポーネントはalLeft/alTop/alRight/alBottomが指定された兄弟コンポーネントを意識して邪魔しないようになっているので、結果としてあまったクライアント領域を選挙することになるのです。

実はこれを利用することによって、例えば(実物のテレビのように)「表示領域の全体とのマージンが全体の大きさに関わらず常に一定」なダイアログも全くコードを記述することなく実現することができます。

具体的にはこうします。

LLayout2.gif (4679 バイト)

とんちっぽいですが、応用がとっても利くので、せっかくだから覚えておきましょう。TeXの利用においては、この考え方がとても重要になってきます。

TeXの場合

さて、ここで割と別畑かつ理系以外にはあんまりメジャーじゃないTeXが登場します。

TeXはたんなる組版ソフトであり、バッチ処理システムですから、インタラクティブなコンテンツを作ることは全く考えられていません。したがって、本来は上で挙げたダイアログエディタと同列に語るべきものではありません。が、コンテンツをレイアウトする仕組みは、実はDelphiのものをさらに進めたもの(歴史的にはこっちが圧倒的に先だけど(笑))といって差し支えないものなのです。

TeXの仕組みを語る前に、Delphi方式の問題点を挙げておきましょう。

現実問題として一番困るのはなんだと思いますか?

それは、「比」が表現できないことです。ことにセンタリング(=左右マージンが1:1)ができないのは致命傷に近いかもしれません。

しかし、TeXでは、絶対座標には触れないままで比が表現できるのです。

Delphiのフォームエディタの延長線上にあるとは述べましたが、実際問題としてTeXのレイアウト構造をDelphiのフォームエディタの延長線上で語るのは困難なので、以下ではTeXのレイアウト構造を直接説明することにします。その説明が理解できれば、私が「Delphiのフォームエディタの延長線上にある」と述べた理由も自ずと分かると思います。

TeXの基本的構造

TeXのレイアウト情報は木構造になっています。その木構造は、大きく分けて「箱」と「ばね」からなっています。文字や線は箱の一種と考えて差し支えありません。

図にするとこんな感じです。

LLayout3.gif (2832 バイト)

この図を仮に木構造を表現するのに便利なXML的に表現するなら、このようになるでしょう。

<hbox width="100pt">
  <glue minwidth="0pt" maxwidth="∞"/>
  <hbox width="30pt"/>
  <glue minwidth="10pt" maxwidth="10pt"/>
  <hbox width="30pt"/>
  <glue minwidth="0pt" maxwidth="∞"/>
</hbox>

※実際のTeXはもっと複雑怪奇ですが、要点を抜き出すとこのようになります。また、言語としてのTeXでは「ばね」ではなく「のり(glue)」という表現を使うのですが、TeXbookでは「ばね」と表現を使うことが多いので、この文章はそれにあわせています。

ここで重要なのは、幅を「無限大」とすることができることです。上の箱に含まれる5つのオブジェクトの中で、一つでも無限大の幅を持つオブジェクトがあれば、結果としてその5つのオブジェクトのリストの幅は無限大より大きくなるはずです。

しかし、そこが「ばね」の「ばね」たる所以です。「箱」の幅は踏んだり蹴ったりしても延びたり縮んだりしませんが、「ばね」の長さはあくまで「最大幅」であり、つっかえた場合はそれ以上のびないものなのです。

また「ばね」の力は(確か指定しない限り)一定ですので、同じ箱の中に複数のばねがあれば、占められるスペースは相殺し合います。上の例では、真ん中の「hbox,glue,hbox」がセンタリングされることになります。

仮に上の例を

<hbox width="100pt">
  <glue minwidth="0pt" maxwidth="∞"/>
  <hbox width="30pt"/>
  <glue minwidth="10pt" maxwidth="10pt"/>
  <hbox width="30pt"/>
  <glue minwidth="0pt" maxwidth="∞"/>
  <glue minwidth="0pt" maxwidth="∞"/>
</hbox>

と書き換えると、「hbox,glue,hbox」を支えるばねの強さが左右で1:2になりますので、マージンにもそれが反映されます。

 

実際のTeXにはこの他にも

などさまざまなファクターがあるため、結果としてめちゃめちゃ複雑です。が、以上で述べたことを理解しているかいないかで学習の進度に絶大な違いがでるはずです。

TeXの処理工程

LaTeXを含めてTeXの処理工程を図示すると、

LLayout4.gif (6503 バイト)

となります。

ここで、上記のように構築した木構造を「論理レイアウト」とみなすと、TeXソースがほぼこれに1対1(といってもアセンブラというよりはマクロアセンブラくらいのフィーリング)で対応するものです。dviファイルは物理レイアウトを示したものです。

結果、LaTeXから見ると、論理文書→論理レイアウト→物理レイアウト、という工程を経て版面が出力されることになります。

TeXの影響

gtk+に見られます。「パッキングボックス」というそうです。

Javaにもなんかあったような気がします。

また私も現在この考え方を用いて、この論理レイアウト構造を動的に変更できるシステム(インタラクティブにするにはどうしても必要)をschemeで構築中です。


(C) 1998 Naoyuki Hirayama. All rights reserved.