エラスティックとページ レイアウト

Curl® 言語でアプレットを作成する場合、Curl® GUI Toolkit のレイアウト システムによってページ レイアウト処理が実行されます。つまり、グラフィカル オブジェクトがレイアウトに合わせて自動的に整列、伸長、および圧縮されます。なお、ユーザー アプリケーションのニーズに合わせてレイアウトを微調整することもできます。
レイアウトにおいて、幅と高さをピクセル(px)単位で指定することには潜在的に問題があります。コンピューター システム上での文字のフォント サイズは、通常ポイント(pt)で指定されます。1ポイントは、72分の1インチと定義されます。1ピクセルは、スクリーンの解像度の単位です。実際のピクセルのサイズは、モニタの物理特性とマシンの設定に依存します。96dpi(ドット パー インチ)の解像度のスクリーン上でレイアウトを作成し、10ptのフォントを使用してテキスト オブジェクトを作成すると仮定してください。テキストを含むのに充分大きくする為に、ピクセル単位を使ってテキスト ボックスの幅と高さを設定します。そして、ユーザーがそのアプレットを解像度が120dpiのスクリーンで実行すると考えてください。その様なスクリーンでは、1ピクセルは、1ポイントよりも小さいので、ユーザーは、ボックスは小さすぎて文字を納めることが出来ず、幾つかの文字がクリップされていることに気がつきます。
Curl 言語は、ネストしたグラフィカル コンテナを使ってオブジェクトを配置します。グラフィカル コンテナの章は、ネストしたグラフィック階層を作成することが出来るコンテナを説明しています。Curlは、HBoxVBoxTableなどのようなコンテナを使用し、Curl レイアウト システムにコンテナ オブジェクトのサイズをそのコンテンツに基づいて計算させるようにすることをお勧めします。この方法で作成されたアプレットは、異なる設定のマシンで実行された時により正確に表示することが出来ます。
この章では、グラフィカル オブジェクトを整列、伸長、圧縮するための基本的な手法について説明します。この章は次のセクションに分かれています。

グラフィカル オブジェクトの整列

グラフィカル オブジェクトの整列は、次の 2 つの観点から考えることができます。
以下のセクションでは、この両方の観点による例を示します。

子の外側起点の指定

各グラフィカル オブジェクトの境界は、その起点を基準にして定義されます。この起点を内側起点と呼びます。内側起点は通常、そのオブジェクトの幾何学上の中心、またはそのオブジェクトの四隅のいずれか (例:左上隅、左下隅) に配置されます。
ネストされたオブジェクトのグラフィック階層の内部において、各グラフィカル コンテナは固有の方法でその子を順序付け、また、それぞれの子の外側起点を使います。グラフィカル オブジェクトの外側起点の位置は移動可能です。つまり、外側起点はグラフィカル オブジェクトの定義の中で設定されるものではありません。なお、外側起点の既定値はそのグラフィカル オブジェクトの内側起点です。
たとえば、HBox はその子オブジェクトを左から順に水平方向に配置します。これにより、それらの子オブジェクトの外側起点の垂直コンポーネントが整列されます。この例から、RectangleGraphicTextFlowBox、および Fill の起点はそれぞれ左下、テキストのベース ライン、および中心になると推測できます。これは、左下、テキストのベース ライン、および中心は、それぞれの外側起点の既定値であるからです。
オブジェクトを整列し直すための最も簡単な方法は、オブジェクトの外側起点をより適切な値に設定することです。オブジェクトの外側起点をより適切な値に設定するには、各グラフィカル オブジェクトの vorigin オプションを設定します。vorigin は、グラフィカル オブジェクトの垂直コンポーネントを示すオプションです。
次の例では、オプション指定の行をコメント アウトするだけで結果を確認できます。

例: vorigin の設定
{HBox
    border-width=3pt,
    border-color="green",
    {RectangleGraphic
        ||vorigin="center",
        width=1cm,
        height=1cm
    },
    {TextFlowBox
        ||vorigin="center",
        border-width=1pt,
        border-color="black",
        "Quick Kangaroo"
    },
    {Fill
        ||vorigin="center",
        background="purple",
        width=2cm,
        height=1.2cm}
}

親の整列プロパティの指定

オブジェクトを整列するもう一つの方法は、親の側からオブジェクトを扱うことです。グラフィカル コンテナの種類によって、halignvalign を使って親の側から整列を実行する方法が異なります。これらのオプションを使う場合、子の起点は無視されます。
グラフィカル コンテナの種類は次のとおりです。
グラフィックhalignvalign
FrameYY
HBoxNY
VBoxYN
TableYY
次の例は、valign オプションを "top" に設定して子の上境界を揃える HBox を示しています。このオプションを使う場合、子の外側起点の値は使われません。そのため、それぞれの子の外側起点がどこに配置されているかは重要ではありません。

例: valign の設定
{HBox
    || set the parent's alignment option
    valign="top",
    border-width=3pt,
    border-color="green",
    {RectangleGraphic
        width=1cm,
        height=1cm
    },
    {TextFlowBox
        border-width=1pt,
        border-color="black",
        "Quick Kangaroo"
    },
    {Fill
        background="purple",
        width=2cm,
        height=1.2cm}
}

オブジェクトの伸長と圧縮

アプレットを作成する際、一般にネストされたグラフィカル オブジェクトを使います。ほとんどの場合、GUI Toolkit では適切なレイアウト処理が自動実行されます。つまり、グラフィカル オブジェクトはその親オブジェクトおよび兄弟オブジェクトを基準にして、適切に伸長または圧縮されます。伸長や圧縮が適切でない場合、子オブジェクトが切り詰められ、スクロールバーが表示されることがあります。TextFlowBox の場合は、テキストが自動改行されます。
一方、既定の動作を使わずに、オブジェクトの幅と高さの動作を指定したい場合もあります。その場合、次のセクションで説明するエラスティックの基本原則について理解しておくとよいでしょう。

エラスティックの基本

グラフィカル オブジェクトの幅と高さをあらかじめ設定された距離と見なすこともできますが (例:8pt1in) グラフィカル オブジェクトの幅と高さの基本となっているのは Elastic です。エラスティックの存在は、ほとんどの場合ユーザーからは見えません。
レイアウト処理の中にレイアウト ネゴシエーションという段階があります。この段階では、グラフィック階層の最上位から各グラフィカル オブジェクトのサイズ設定が検査されます (通常は、最初に幅設定が検査されます)。このサイズ設定がそのグラフィカル オブジェクトの親に渡されます。
グラフィカル オブジェクトの width オプションおよび height オプションは、そのグラフィカル オブジェクトの幅設定と高さ設定の一つの側面を表しています。技術的に見ると、これらのオプションは Dimension 型ですが、実際にはさまざまなデータ型を利用できます。これらのデータ型は、内部的に Dimension オブジェクトに変換されます。Dimension は内部的には Elastic、より具体的に言えば OriginElastic として解釈されます。
したがって、エラスティックはあるグラフィカル オブジェクトの親コンテナ、兄弟オブジェクト、子オブジェクトを基準にしてそのグラフィカル オブジェクトの動作を定義します。
この複雑な流れを単純化するため、レイアウト ネゴシエーションの結果、各オブジェクトにはあるサイズが割り当てられます。このサイズは、そのオブジェクトの設定と一致していない場合もあります。この設定は、エラスティックのフォームの中で記述します。

エラスティック パラメータ

エラスティックは次の 6 個のパラメータによって制御されます。
エラスティック パラメータについて検討する場面としては、次のようなケースが考えられます。
グラフィカル オブジェクトの幅オプションを Distance (例:3in) に設定した場合、結果的に、変更不能なエラスティック (preferred-size = 3 インチ) を指定したことになります。 stretch-order と compress order の値はきわめて低くなります。
以下のセクションでは、例を使ってstretch-orderと stretchness について説明します。同様に、 compress order と compressibility についても説明します。それぞれの例では、make-elastic を呼び出して Elastic オブジェクトを作成します。また、上述のパラメータを引数として指定します。

起点のエラスティック

この章では、「エラスティック」という用語を使ってグラフィカル オブジェクトの幅と高さの設定を表していますが、これらの設定は OriginElastic として実装されます。OriginElastic は、2 つの Elastic コンポーネントで構成されており、この 2 つのコンポーネントはグラフィカル オブジェクトの内側起点によって区切られています。
グラフィカル オブジェクトの幅設定における 1 つ目のコンポーネントは左エラスティック、2 つ目のコンポーネントは右エラスティックです。同様に、グラフィカル オブジェクトの高さ設定における 1 つ目のコンポーネントは内側起点の上側のエラスティック、2 つ目のコンポーネントは内側起点の下側のエラスティックです。
前述のとおり、グラフィカル オブジェクトの内側起点は通常、四隅のいずれかまたは幾何学上の中心に配置されます。内側起点が四隅のいずれかにある場合、いずれかのコンポーネントが 0 であることを意味します。このオプションに設定されたエラスティック パラメータは、1 つのコンポーネントにのみ適用されます (幅と高さ)。このケース (多くのグラフィカル オブジェクトに適用されます) において、幅が伸長可能な場合、このグラフィカル オブジェクトの幅はその内側起点の一方の側に向けて伸長されます。
グラフィカル オブジェクトの内側起点が四隅のいずれにも配置されていない場合、1 つ目と 2 つ目のコンポーネントのエラスティックを指定することにより、そのグラフィカル オブジェクトを内側起点の各側に不均等に伸長/圧縮することができます。

stretch-order

次の例は、子グラフィカル オブジェクトを 2 つ含む HBox です。それぞれのグラフィカル オブジェクトの幅は、make-elastic を明示的に呼び出すことによって指定されています。次の点に着目してください。

例: stretch-order の違い
{value
    {HBox
        width={make-elastic
                  preferred-size=10cm},
        {CommandButton
            width={make-elastic
                      stretch-order=30,
                      stretchiness=100
                  }
        },
        {CheckButton
            background="lime",
            width={make-elastic
                      stretch-order=40,
                      stretchiness=400
                  }
        }
    }
}

stretchness

次の例は、子グラフィカル オブジェクトを 2 つ含む HBox です。それぞれのグラフィカル オブジェクトの幅は、make-elastic を明示的に呼び出すことによって指定されています。次の点に着目してください。

例: エラスティックに対する幅の明示的な設定
{value
    {HBox
        width={make-elastic
                  preferred-size=10cm},
        {CommandButton
            width={make-elastic
                      stretch-order=30,
                      stretchiness=100
                  }
        },
        {CheckButton background="lime",
            width={make-elastic
                      stretch-order=30,
                      stretchiness=400
                  }
        }
    }
}

Dimension からエラスティックへの変換

Elastic 演算 (例:Elastic.setElastic.add) のオペランドとして Dimension を指定した場合、その Dimension は次の規則に従って、対応する Elastic に変換されます

Dimension から OriginElastics への変換

Dimension 引数を OriginElastic に変換する演算 (例:OriginElastic.set) のオペランドとして Dimension を指定した場合、次の変換規則が適用されます。
上述の規則に従って DimensionOriginElastic に明示的に変換するには、dimension-to-origin-elastic を使います。

エラスティックを使ったページ レイアウト手法

Curl® GUI Toolkit には、オブジェクトの基礎となるエラスティックを調整するためのさまざまな手段が用意されています。以降のセクションではこれらの手段について説明します。
以降のセクションでは、エラスティックの使用方法、およびさまざまな手法の使用例について説明します。

width/height オプションの設定

width オプションおよび height オプションが一般に設定されるのは、次のオブジェクトやプロシージャです。
グラフィカル オブジェクトの width オプションまたは height オプションを設定する際、値がエラスティックでない場合は内部的にエラスティックに変換されます。オブジェクトの幅/高さを直接エラスティックに設定するには、幅/高さを次のプロシージャのいずれかに設定します。
エラスティックを使う手段としては、次のプロシージャを使うという方法もあります。
テキストが表示される場合、そのテキストは TextFlowBox の内部にラップされ、グラフィック階層の中に配置されます。TextFlowBox の一部のエラスティック特性はテキストのフローに対応しているので、ここで説明します。
この情報が特に役立つのは、トップレベル ページのレイアウト処理を行っている場合です。なぜなら、ブラウザにおけるトップレベル オブジェクトはドキュメントであり、利用可能なそれぞれのドキュメント スタイルは別の種類のオブジェクトであるからです。DefaultDocument および TocDocument は、 ScrollBox の中でラップされる TextFlowBox です。PlainDocumentTextFlowBox ではありません。詳細については、「ページ内におけるエラスティック」を参照してください。
text-width-display の使用例を次に示します。

例: text-width-display の使用
{paragraph
    paragraph-left-indent=1cm,
    paragraph-right-indent=1cm,
    {paragraph
        font-weight="bold",
        paragraph-justify="center",
        Regional Championship}
    {text-width-display
        {HBox
            {paragraph color="purple",
                {underline Purple Team}{br}
                Violet Schneider{br}
                Lila Lee
            },
            {Fill},
            {paragraph color="green",
                {underline Green Team}{br}
                Nina Gruengarten{br}
                Ching Yi
            }
        }
    }
}

パディング用としての Fill の使用

Fill はパディング用としてよく使われます。理由は、クラスの stretch-order と compress-order の既定値がきわめて高い (40) ためです。
コンテナ内のオブジェクトをコンテナの各境界に接するように配置する場合、コンテナ内のスペースを埋めるために Fill を使用できます。Fill の既定の stretch-order は他のほとんどのグラフィカル オブジェクトの既定の stretch-order よりも高いので、オブジェクトを伸長するというよりも、空き領域をスペースで埋めていくことができます。
他のグラフィカル オブジェクトと同様、Fill の幅と高さをエラスティックに明示的に設定できます。

例: Filltext-width-display の併用
{text-width-display
    {HBox background={LinearGradientFillPattern
                         {Fraction2d 0.0, 0.0},
                         {Fraction2d 1.0, 0.0},
                         {Spectrum.from-endpoints
                             "#80b4b3",
                             "white"
                         }
                     },
        {text Thank you for using Curl IDE},
        {Fill} ,
        {link href={url "http://www.curl.com/"}, SCSK Corporation.}
    }
}

コンテナの hstretch? オプションおよび vstretch? オプションの設定

一部のグラフィカル コンテナには、hstretch? または vstretch? というローカル オプションが設定されています。このオプションが true に設定されている場合、子オブジェクトは伸長されます。
これらのオプションの既定値は false です。このオプションが false に設定されている場合、 stretch-order が 25 より高い子オブジェクトだけが伸長されます。例については、Frame.hstretch? を参照してください。
このようなコンテナ クラスは次のとおりです。

OverlayBox の使用

グラフィック階層の中に、同時に 1 つだけ表示すべきオブジェクトがある場合、OverlayBox を使用できます。OverlayBox は、その子をそれ自体の境界まで伸長します。つまり、既定では最も前面にある (非透過) オブジェクトだけが見えることになります。

複数の整列手法を組み合わせて使った例

このセクションでは、次の整列オプションとエラスティックを使って整列を行う例を示します。

例: オプションとプロシージャを使った Elastics の利用
{define-proc {control-box}:VBox
    {return
        {spaced-vbox
            || use make-elastic to set a minimum size
            width={make-elastic
                      minimum-size=2in,
                      preferred-size=2in,
                      stretch-order=30,
                      compress-order=30
                  },
            margin=0.5cm,
            border-width=1pt,
            border-color="black",
            {text How do you feel about sharks?},
            {RadioFrame
                {VBox
                    {RadioButton label="I love sharks."},
                    {RadioButton label="I am terrified of sharks."},
                    {RadioButton label="Other"}
                }
            },
            || use hcenter for padding on both sides of the "Vote" button
            {hcenter {CommandButton label="Vote"}}
        }
    }
}

{HBox
    || use valign to vertically align the 2 children by their centers
    valign="center",
    margin=0.5cm,
    background="#aaaaff",
    {TextFlowBox margin=0.5cm,
        {center {bold Warning to Tourists}},
        "Do not pat the heads of the sharks. They are not pets.
         This is not a petting zoo.
         Do not stand on the dead whales. They are being consumed
         by the sharks. Thank you for your attention."
    },
    {control-box}
}

|| text-width-display stretches the sum of the objects to the
|| width of the enclosing TextFlowBox (in DefaultDocument or View)
{text-width-display
    {HBox
        || use valign to vertically align the 3 children by their centers
        valign="center",
        margin=0.5cm,
        background="#aaaaff",
        {Frame margin=0.5cm,
            {image
                source={url "../../default/images/generic.gif"},
                width=2in, height=2in}
        },
        || add Fill so that the control box does not get stretched
        {Fill},
        {control-box}
    }
}

ページ内におけるエラスティック

ブラウザに表示されるアプレットは、ドキュメントの中に含まれています。ドキュメントには 3 種類のスタイルがあります。3 種類のドキュメント スタイルのいずれかを指定するには、document-style を使います。このプロシージャを明示的に呼び出さない場合、既定で DefaultDocument が使われます。
このため、アプレットの伸長/圧縮動作は使用しているドキュメント スタイルによって異なります。アプレットがテキスト ベースの場合、DefaultDocument または TocDocument を使用すべきです。なぜなら、TextFlowBox によってテキストの適切な高さが設定され、また、テキスト全体を ScrollBox で囲めば必要時にスクロールバーが表示されるからです。
アプレットが、最上位レベルでページの垂直方向全体に広がるオブジェクトである場合、そのオブジェクトの祖先が TextFlowBox であってはいけません。つまり、DefaultDocument または TocDocument は使用しないでください。その代わり、PlainDocument を使えば必要な動作を実行できます。PlainDocumentFrame であるからです。
Fill を使ってグラフィカル オブジェクトを垂直方向に中央揃えし、また VBox.halign オプションを設定してグラフィカル オブジェクトを水平方向に中央揃えする例については、plain-document.curl をクリックしてください。
DefaultDocument においてグラフィカル オブジェクトを水平方向に中央揃えする例については、default-document.curl をクリックしてください。Fill を使ってグラフィカル オブジェクトを垂直方向に中央揃えしようとしても、うまくいきません。グラフィカル オブジェクトを垂直方向に中央揃えするには、PlainDocument を使う必要があります。

テーブルにおけるエラスティック

Table を使う場合、テーブル全体の幅およびテーブルの各列のエラスティックを制御できます。
column-prototyperow-prototype を使って、幅などのオプションを設定できます。
オプション (例:幅) を設定するには、*-prototype を追加します。
次の例を見てください。

例: Table および列の幅を Elastic に設定
{let t:Table=
    {Table
        || Ensure that table's width stretches when the
        || dialog is stretched by the user.
        width={add-stretch},
        background="#8888aa",
        {column-prototype
            || Prevents the first column from stretching
            || when the table stretches.
            width={make-elastic
                      minimum-size=1in,
                      preferred-size=1in,
                      stretch-order=10,
                      compress-order=10,
                      compressibility=50
                  },
            {cell-prototype {text UserID:}},
            {cell-prototype {TextField}},
            {cell-prototype colspan=2,
                {CommandButton
                    || Stretches the "Need Hint" CommandButton
                    || to the left and right of its inside origin.
                    width={add-stretch before?=true,
                              after?=true},
                    label="Need Hint"}
            }
        },
        {column-prototype
            || Prevents the 2nd column from stretching
            || when the table stretches.
            width={make-elastic
                      minimum-size=1in,
                      preferred-size=1in,
                      stretch-order=10
                  },
            {cell-prototype {text Password:}},
            {cell-prototype {PasswordField}},
            {skip}
        },
        {column-prototype
            || Without resizing, the width of the 3rd column is 2in
            width={make-elastic minimum-size=2in},
            {cell-prototype {text From:}},
            {cell-prototype {TextField}}
        },
        {column-prototype
            || Without resizing, the width of the 4th column is 2in
            width={make-elastic minimum-size=2in},
            {cell-prototype {text To:}},
            {cell-prototype {TextField}},
            {cell-prototype
                || Right align the "Search" button within
                || its cell.
                {text-width-display
                    {HBox {Fill}, {CommandButton label="Search"}}
                }
            }
        }
    }
}

{CommandButton
    label="stretchy table in resizable dialog",
    {on Action do
        {let dialog:Dialog=
            {Dialog
                t
            }
        }
        {dialog.show style="resizable", title="Ticket Search"}
    }
}

例: text-width-displayTable を配置
{text-width-display
    {Table columns=2,
        width={make-elastic minimum-size=2in, stretch-order=40},
        background="#8888aa",
        {row-prototype
            {cell-prototype
                colspan=2,
                {text Show me the cheapest round trip ticket for:}
            }
        },
        {row-prototype
            {cell-prototype {text From:}},
            {cell-prototype {TextField}}
        },
        {row-prototype
            {cell-prototype {text To:}},
            {cell-prototype {TextField}}
        }
    }
}

トラブルシューティング

この章の最初で説明したように、エラスティックはグラフィカル オブジェクトの幅 の Dimension と高さの Dimension の基礎となります。そのため、グラフィック階層においてオブジェクトをネストする場合、オブジェクトは通常、開発者またはユーザーの予想どおりに伸長または圧縮されます。
既定の伸長/圧縮動作が自分の設計に合わない場合、以下に説明する手法を使って動作を調整します。
子は親の境界まで伸長しません。
TableCommandButton を含む VBox の例を次に示します。この VBox は、子のサイズに合わせて伸長されます。コメントを解除して次のことを実行してください。

例: 子を親の境界まで伸長
{VBox
    ||width=2in,
    ||hstretch?=true,
    spacing=1.5cm,
    border-width=1pt,
    border-color="navy",
    {Table columns=2,
        {text Name:},
        {TextField},
        {text State:},
        {DropdownList
            "MA",
            "RI",
            "NH"}
    },
    {CommandButton
        label="Reset all entries"}
}

レイアウト システム

レイアウト ネゴシエーション

Curl アプレットのグラフィック レイアウトは、レイアウト ネゴシエーションのプロセスによって決まります。このプロセスは、グラフィック階層内の Graphic オブジェクトにかかわっています。
レイアウト ネゴシエーションでは、まず各オブジェクトの幅と高さの設定を調べ、最後に幅と高さを各 Graphic に設定します。幅と高さのどちらを先に処理してもかまいませんが、幅を先に処理するのが一般的です。
幅を先に処理するレイアウト ネゴシエーションは通常、グラフィック階層内のそれぞれの Graphic において Graphic.get-width-preference が呼び出された時点で開始します。その後各グラフィックの幅が適切かどうかが検査されます。
グラフィック階層内のすべての Graphic の幅設定が収集された後、グラフィック階層内のオブジェクトに設定される幅が計算されます。この幅の値は、レイアウト ネゴシエーションの第 2 段階で使われます。第 2 段階では、第 1 段階で幅が設定されている各グラフィックに適した高さが検査されます。この第 2 段階は、グラフィック階層内のすべての Graphic において Graphic.constrain-width が呼び出された時点で開始します。検査された高さ設定を使って、最終段階で Graphic に設定する高さが計算されます。最終段階では、各 Graphic の幅と高さが設定されます。この最終段階は、各 Graphic において Graphic.set-size が呼び出された時点で開始します。
高さを先に処理するレイアウト ネゴシエーションは、幅を先に処理するレイアウト ネゴシエーションとおおむね似ています。高さと幅の扱いが入れ替わるだけです。つまり、第 1 段階は Graphic.get-height-preference で開始し、第 2 段階は Graphic.constrain-height で開始します。第 3 段階は Graphic.set-size で開始します。
一部のレイアウト ネゴシエーションでは、第 1 段階が省略されます。ただし、第 2 段階と第 3 段階では、各 Graphic に対して新しいサイズが通知されるので、必ず実行されます。