選択

要約:
  • 選択 API は、個別選択、範囲選択、テキストコントロールの選択をサポートしています。
  • SelectionContext オブジェクトによって、各選択のコンテキスト / スコープを生成することができます。イベントにより、現在の選択項目を保持し、ユーザー操作と選択項目間の通信を管理することができます。イベントを任意のグラフィックに関連付けて、そのグラフィカルな子孫を選択できるようにします。
  • DiscreteGraphicSelectionFrame および GuiRangeSelectionFrame は、選択コンテキストでグラフィック階層を囲む、使いやすい Frame です。
  • TextFieldPasswordFieldTextAreaRichTextArea および ComboBox などのテキスト コントロールは、その中に含まれるテキストを管理する組み込みの選択コンテキストを提供します。

概要

インタラクティブな web ページやアプリケーションのユーザーは、マウスまたはキーボードを頻繁に使用して選択を行ないます。たとえばショッピング カートにアイテムを追加したり、ファイルを表すアイコンを選択して削除したり、テキストをコピーしてテキスト エディタに貼り付ける場面がこれに含まれます。このような対話をサポートする全体的な機能は、選択(Selection)と呼ばれます。選択の主なタイプは個別選択と範囲選択です。
Curl の選択構造は次の主要な機能領域、選択コンテキスト、選択およびコマンドで構成されています。

簡単な個別選択の例

DiscreteGraphicSelectionFrame は、個別選択機能を持つグラフィカル オブジェクトです。アプリケーションに個別選択を組み込む簡単な方法を提供します。基本的なステップは次のとおりです。
  1. DiscreteGraphicSelectionFrame にグラフィック階層をラップします。選択 draw-style をキーワード引数として設定できます。
  2. フレーム内で選択可能にする各グラフィックに対して DiscreteGraphicSelectionFrame.graphic-selectable オプションを設定します。
    set graphic-selectable = {GraphicSelectable}
    
  3. 既定では multiple-selection-enabled? は true で、複数項目の選択が許可されています。複数項目の選択を許可しない場合はこれを false に設定します。
グラフィックスと同様にシェイプも選択に使用できます。 「シェイプ」 の章の、 「選択」 を参照してください。

例: DiscreteGraphicSelectionFrame の使用
{value
    let triangle:RegularPolygonGraphic =
        {RegularPolygonGraphic
            graphic-selectable={GraphicSelectable},
            height = 2cm,
            width = 2cm,
            sides = 3
        }

    let rectangle:RegularPolygonGraphic =
        {RegularPolygonGraphic
            graphic-selectable={GraphicSelectable},
            height = 2cm,
            width = 2cm,
            sides = 4
        }

    let pentagon:RegularPolygonGraphic =
        {RegularPolygonGraphic
            graphic-selectable={GraphicSelectable},
            height = 2cm,
            width = 2cm,
            sides = 5
        }
    let info-vbox:VBox={VBox}
    let cb:CommandButton={CommandButton label="Clear Selections"}
    let dgsf:DiscreteGraphicSelectionFrame =
    {DiscreteGraphicSelectionFrame
        {spaced-hbox
            color = {FillPattern.get-blue},
            triangle,
            rectangle,
            pentagon
        },
        {on sc:SelectionChanged at dgsf:DiscreteGraphicSelectionFrame do
            {info-vbox.clear}
            set cb.enabled?=not dgsf.selection.empty?
            {for s:GraphicSelectable in dgsf.selection.items do
                {info-vbox.add
                    {format
                        "You have selected a polygon with %d sides. \n",
                        (s.graphic asa RegularPolygonGraphic).sides

                    }
                }
            }
        }
    }
    {cb.add-event-handler
        {on Action do
                {dgsf.select-nothing}
                {info-vbox.clear}
        }
    }
    {spaced-vbox
        dgsf,
        {center {value cb}},
        info-vbox
    }
}

簡単な範囲選択の例

ほとんどの Web ページでは、ユーザーがマウスを使用してテキストの選択やコピーができるように範囲選択をサポートしています。範囲選択は、特別なタイプのテキスト選択です。選択への書き込みは許可されていないので、貼り付け操作はできません。
TextAreaRichTextArea および TextField などのオブジェクトはテキストコントロールのため、範囲選択を使用しません。代わりに、テキストコントロールは、テキストを操作する固有のビルトイン選択メカニズムがあります。
選択中に個々の領域 (コンテキスト) を区別する場合に範囲選択を使用するのが一般的です。たとえば、Web サイトでユーザーがマウスを使って新聞記事のテキストを選択するときに、ページの広告の領域上をマウスが移動してもこの領域が選択範囲から除外されるようにする場合などです。同様に、ユーザーが広告領域内でマウスを押さえると、選択対象が広告の選択コンテキストに限定されるようにする場合もあります。
GuiRangeSelectionFrame は、範囲選択コンテキストを確立するための便利な方法を提供します。基本ステップを次に示します。
  1. GuiRangeSelectionFrame にグラフィック階層をラップします。
  2. コントロールを使用してその bound-command プロパティを適切なコマンドに設定して、選択項目の選択タスクを実行します。
範囲選択で領域のスコープを定義する例を次に示します。次の点に注意してください。

例: GuiRangeSelectionFrame の使用
{value
    let grsf:GuiRangeSelectionFrame =
        {GuiRangeSelectionFrame
            {TextFlowBox
                {paragraph font-weight="bold",
                    Sample Text},
                {paragraph
                    Left
                    {VBox
                        vorigin="center",
                        halign="center",
                        spacing=8pt,
                        "Up",
                        "Down"
                    }
                    Right
                }
            }
        }
    let text-area:TextArea =
    {TextArea
        width=3in
    }

    {spaced-hbox border-width=1px,
        border-color="black",
        {spaced-vbox
            grsf,
            {CommandButton
                label="Clear Range Selection",
                bound-command={grsf.get-command "select-nothing"}
            }
        },
        {VBox
            {paragraph font-weight="bold", Text Area},
            {paragraph Copy text on the left and paste it below.},
            text-area
        },
        {VBox
            height=1in,
            {Fill},
            {CommandButton
                label="Select All Text in Text Area",
                bound-command={text-area.get-command "select-all"},
                takes-focus?=false
            },
            {CommandButton
                label="Delete Selected Text in Text Area",
                bound-command={text-area.get-command "delete"},
                takes-focus?=false
            }
        }
    }
}

選択コンテキスト

選択コンテキストでは、選択時に含まれる項目と除外される項目を定義します。たとえば、個別選択アプリケーションでユーザーがデスクトップ アイコンのグループを投げ縄ツールで選択することがあります。このような場合に、実行可能ファイルのアイコンが投げ縄ツールの選択範囲に含まれていても選択から除外するような選択コンテキストを確立することができます。
既定では、すべての View にはビュー全体で 1 つの範囲選択を確立するトップレベルの範囲選択コンテキストがあります。これは、既定ではアプレットには 1 つの範囲選択コンテキストがあるということを意味します。ただし、たいていの場合は Web ページ内のすべてのアイテムが同じコンテキストに属さないように、ネストされた範囲選択コンテキストを定義します。
たとえば、トップレベルにテキストと画像を持ち、選択コンテキストを指定しない簡単なアプレット(例:「単純なアプレット」)を作成するとします。 テキストと画像は同じコンテキストに属しています。マウスを使って範囲を選択してみてください。

選択コンテキストの確立

selection-context は、グラフィカル オブジェクトが属する選択コンテキストを指定するための非ローカル オプションです。グラフィカル オブジェクト自体が SelectionContext サブクラスである場合、既定でこの非ローカル オプションは通常 self に設定されます。
selection-context は非ローカル オプションなので、この値が設定されていない場合は、グラフィカルな親 (または一番近い祖先) の値を取り、オブジェクトをその親 (または祖先) と同じ選択コンテキストに含めることになります。
ほとんどの場合、選択コンテキストを直接 SelectionContext に設定する必要はないでしょう。代わりに、暗黙的に指定された選択コンテキストからオブジェクトおよびその埋め込み階層を除外するか、またはオブジェクト自体の埋め込み選択コンテキストを確立します。
これで、ネストされた選択コンテキストを "埋め込まれた" コンテキスト内に確立できるようになります。
ネストされた選択コンテキストの例を次に示します。

例: ネストされた選択コンテキスト
{value
    {spaced-hbox
        {VBox
            {text Outer Selection Context Starts},
            {radio-buttons
                "Mike Harris",
                "Sam Richardson"}
        },
        {VBox font-style="italic",
            selection-context=null,
            {text Excluded Hierarchy},
            {GuiRangeSelectionFrame
                {spaced-vbox color="brown",
                    {text Inner Selection Context},
                    {text Part of Inner Selection Context}
                }
            },
            {text End of Excluded Hierarchy}
        },
        {VBox
            {text Still in the outer selection context ...},
            {radio-buttons
                "Mike Harris",
                "Sam Richardson"},
            {text Outer Selection Context Ends}
        }
    }
}

個別選択

個別選択は通常、クリック可能なグラフィカル オブジェクト (個別グラフィック選択) として実装されます。ユーザーはオブジェクトをクリックするか投げ縄ツールを使用して個別選択を行ないます。次のプロパティは、個別選択をサポートする UI の作成の必要なものです。

グラフィックを選択可能にする

グラフィカル オブジェクトが個別選択をサポートするには、その Graphic.graphic-selectable オプションを GraphicSelectable オブジェクトに設定する必要があります。既定値の null では、グラフィカル オブジェクトは個別選択をサポートしません。
GraphicSelectable は、ユーザーによるクリックまたは 四角形選択 (投げ縄) でグラフィックをキャプチャできるかどうかを決定する情報をカプセル化しています。特別な動作を必要としない場合は、グラフィカル オブジェクトを新しい GraphicSelectable のインスタンスに設定するだけ十分です。それ以外の場合は、GraphicSelectable をサブクラス化してメソッドをオーバーライドすることを推奨します。

単一および複数選択

個別選択コンテキストでは DiscreteSelectionContext.multiple-selection-enabled? プロパティ の既定値は true なので、グラフィカル オブジェクトの複数選択が可能です。この場合、ユーザーは CTRL+ クリックの操作で複数選択を行なうことができます。
プロパティを false に設定して複数選択を不可能にする場合もあります。

個別選択の外観の設定

個別選択の結果としてグラフィカル オブジェクトがどのように表示されるかは、その選択コンテキスト上で設定されます。DiscreteGraphicSelectionContext.draw-styleGraphicSelectableDrawStyle 列挙型の要素に設定できます。
独自の描画スタイルに設定する場合は、draw-style を "none" に設定してから SelectableAdded および SelectableRemoved のイベント ハンドラを記述します。これらは、特定の Selectable で選択コンテキストの選択に対して追加または削除が実行されると発生します。
次の例では、赤、緑、青の個々のグラフィック階層の周囲に DiscreteGraphicSelectionFrame をラップし、直接その上で draw-style を設定できるようにしています。個々の階層にある選択可能な項目をクリックする、または色々なアイテムを各ボックスにドラッグして、それぞれの draw-style 値がどのような結果になるか確かめてみてください。 CTRL+ クリックで選択前の draw-style に戻ります。
描画スタイルを 反転 した際の振る舞いは、どのレンダラーを使用しているかに依存します。 特に、Windows の既定のレンダラーは 色の反転といった能力を持っていないため、クロスハッチ・パターンのオーバーレイを使用するでしょう。 色の反転がアプリケーションに必要な場合は、 set-rendering-mode プロシージャを使用し、 RenderingMode.normal を選択してください。

例: draw-style の設定
{define-proc {dgsf draw-style:GraphicSelectableDrawStyle, border-color:FillPattern}:DiscreteGraphicSelectionFrame
    {return
        {DiscreteGraphicSelectionFrame
            draw-style=draw-style,
            {spaced-vbox
                margin=0.5cm,
                halign="center",
                border-width=3pt,
                border-color=border-color,
                color=border-color,
                {small
                    draw-style\=\"{value draw-style.name}\"},
                {image width=1in, height=1in,
                    graphic-selectable={GraphicSelectable},
                    source={url "curl://install/docs/default/images/adria.jpg"}},
                {EllipseGraphic
                    graphic-selectable={GraphicSelectable},
                    width=0.5in, height=0.5in,
                    fill-color=border-color},
                {TextDisplay
                    graphic-selectable={GraphicSelectable},
                    value = draw-style.name}
            }
        }
    }
}
{HBox spacing=2cm,
    {dgsf "mask", "red"},
    {dgsf "invert", "green"},
    {dgsf "none", "blue"}
}

テキスト コントロール内での選択

テキスト コントロールは書き込み可能なコントロールです。つまり、コントロールはテキストを保持および管理して、ユーザーによるテキストの入力と操作 (選択、コピー、貼り付け、削除) を可能にしています。TextFieldPasswordFieldTextAreaRichTextArea および ComboBox はすべてテキスト コントロールです。
このテキスト管理機能によって、テキスト コントロールはそれ自体が選択コンテキストになります。各テキスト コントロール クラスは WritableTextSelectionContext を継承します。既定では、このようなグラフィカル オブジェクトの selection-context オプションは self に設定されていて、周囲のグラフィック階層とは別に選択コンテキストを作成することができます。

組み込まれた選択コンテキストについて

テキストコントロールの組み込まれている選択コンテキストの例を次に示します。ここでは selection-context 非ローカル オプションの明示的な設定はありませんが、マウスで範囲選択を行なうと、確立されているいくつかの選択コンテキストを確認することができます。

例: テキストコントロールの選択コンテキスト
{spaced-hbox
    {spaced-vbox
        {text Please Enter Your User Name and Password.},
        {Table columns=2,
            valign="bottom",
            {bold User Name:}, {TextField width=1in},
            {bold Password:}, {PasswordField width=1in}
        }
    },
    {spaced-vbox width=3in,
        {text Describe the problem you have encountered:},
        {TextArea
            value="text area text area"
        }
    }
}

選択がクリアされるのを回避

マウスまたはキーボードによって、別の選択コンテキストをアクティベートすることにより、 選択コンテキストの状態が変化することがあります。選択コンテキストは、 非アクティブとして表示される可能性があります。テキストコントロールの場合は、 選択対象自体を落下していまうことがあります。
ボタンのような他のコントロールを使用してテキストコントロール内の選択対象を操作する場合、 テキストコントロールが、非アクティブになり選択内容がクリアされることを最初に回避する必要があります。 そのためには、対象のコントロールの takes-focus?false に設定します。

次の例でこの方法を示します。

例: takes-focus?false に設定
{value
    let name-tf:TextField = {TextField width=1in}
    let t:Table=
    {Table columns=2,
        valign="bottom",
        {bold Name:}, name-tf
    }

    let ok:CommandButton =
        {CommandButton
            label="OK"}

    let select-all:CommandButton =
        {CommandButton
            label="Select all text",
            bound-command={name-tf.get-command "select-all"},
            takes-focus?=false
        }

    let delete:CommandButton =
        {CommandButton
            label="Delete selected text",
            bound-command={name-tf.get-command "delete"},
            takes-focus?=false
        }

    {spaced-vbox
        {text Please enter your name},
        t,
        {HBox
            {Fill},
            ok,
            select-all,
            delete
        }
    }
}

選択とコントロールの使用

Curl コントロールとメニュー操作により、一般的な選択タスクをプログラムとして簡単に実行することができます。さらにこれらのコントロールは、現在の選択状態に応じて無効または有効になります。

コマンドと選択コンテキストをコントロールにバインドする

選択コンテキストは、コマンド アーキテクチャを利用して選択タスクを実行します。
選択コンテキストの SelectionContext.get-command メソッドを呼び出し、有効なコマンド文字列を入力して Command を取得します。
次に、返された Command をコントロールの bound-command プロパティに代入します。
有効なコマンド文字列については、次の表を参照してください。
個別選択範囲選択テキストの選択
"select-all"YYY
"select-nothing"YYY
"copy"NYY
"cut"NNY
"delete"NNY
"paste"NNY
"find"NYN

個別選択

次の例では、2 つのグラフィカル オブジェクトは DiscreteGraphicSelectionContext のサブクラスである、DiscreteGraphicSelectionFrame にラップされています。 コマンド ボタンは、DiscreteGraphicSelectionFrame.get-command の呼び出しの結果にバインドされ、コマンド実装の名前を文字列として与えます。

例: select-all および select-nothing の使用
{value
    let dgsf:DiscreteGraphicSelectionFrame=
    {DiscreteGraphicSelectionFrame
        {spaced-vbox
            {image width=2cm, height=2cm,
                source={url "curl://install/docs/default/images/brick-staggered.jpg"},
                graphic-selectable={GraphicSelectable}
            },
            {image width=2cm, height=2cm,
                source={url "curl://install/docs/default/images/leaves.jpg"},
                graphic-selectable={GraphicSelectable}
            }
        }
    }

    let cb-all:CommandButton=
    {CommandButton
        label="Select All",
        bound-command={dgsf.get-command "select-all"}
    }

    let cb-nothing:CommandButton=
    {CommandButton
        label="Select Nothing",
        bound-command={dgsf.get-command "select-nothing"}
    }

    {spaced-hbox
        dgsf,
        {spaced-vbox
            cb-all,
            cb-nothing
        }
    }

}

Gui の範囲選択とテキストの選択

次の例では、TextFlowBox は GuiRangeSelectionContext のサブクラスである GuiRangeSelectionFrame にラップされます。[paste] ボタン以外のすべてのコマンド ボタンは TextFlowBox にバインドされ、[paste] ボタンは TextField にバインドされます。ここでは、[paste] コマンドは書き込み可能テキストの選択コンテキストにのみ実装されます。

例: copy および paste の使用
{value
    let tf:TextField =
        {TextField
            value="text field text field."}

    let grsf:GuiRangeSelectionFrame=
    {GuiRangeSelectionFrame
        {TextFlowBox
            "Lots of text in a TextFlowBox"
        }
    }

    let cb1a:CommandButton =
        {CommandButton
            label="Select Text",
            bound-command={grsf.get-command "select-all"}
        }
    let cb1b:CommandButton =
        {CommandButton
            label="Clear Selection",
            bound-command={grsf.get-command "select-nothing"}
        }
    let cb2:CommandButton =
        {CommandButton
            label="Copy",
            bound-command={grsf.get-command "copy"}
        }
    let cb3:CommandButton =
        {CommandButton
            label="Paste in Text Field",
            bound-command={tf.get-command "paste"}
        }

    {HBox
        {value grsf},
        {VBox
            {HBox cb1a, cb1b},
            cb2,
            cb3
        },
        {value tf}
    }
}

選択アーキテクチャ

SelectionContext クラスは、選択アーキテクチャで主要な役割を果たします。SelectionContext は抽象基本クラスです。このサブクラスでは、アーキテクチャがサポートする種々の選択を実装します。
SelectionContext のサブクラスは、Selection オブジェクトを保持します。同様に、Selection は並列クラス階層の抽象クラスです。各サブクラスは Selection を 1 つのタイプとして実装します。このタイプは特定のタイプの SelectionContext によって保持されます。
選択アーキテクチャでの主要なクラスは次のとおりです。
個別グラフィック選択範囲選択テキストコントロールの選択
DiscreteGraphicSelectionContextGuiRangeSelectionContextWritableTextSelectionContext
DiscreteSelectionGuiRangeSelectionTextSelection
DiscreteGraphicSelectionFrameGuiRangeSelectionFrame
GraphicSelectable
選択において発生するイベントは SelectionEvent クラス階層に属します。SelectionContext クラスは、それ自体または対応する Selection が変更するときにイベントを発生させます。

クラス階層

以下のセクションでは、選択コンテキストと選択クラス階層について説明します。

SelectionContext

SelectionContext クラス階層を次に示します。ここで、SelectionContext はすべて CommandContext であり、したがって特定の目的に合わせて (選択) コマンドを作成して保存することができます。たとえば、WritableTextSelectionContext は、他の選択コンテキストではサポートしない "paste" コマンドをサポートします。
CommandContext

SelectionContext




DiscreteSelectionContext

DiscreteGraphicSelectionContext
TextSelectionContext




WritableTextSelectionContext GuiRangeSelectionContext

Selection

Selection クラス階層を次に示します。
Selection




DiscreteSelection
TextSelection

GuiRangeSelection

選択コンテキストとの対話

SelectionContext は、項目を選択する領域 (コンテキスト) の境界を定義します。コンテキストの外側またはネストされたコンテキスト内の項目は、このコンテキストに存在しません。

グラフィックの既定の選択コンテキスト

すべてのグラフィカル オブジェクトには selection-context 非ローカル オプションがあります。各オブジェクトの selection-context オプションの値は、オーバラップしていない、潜在的にネストされた選択コンテキストがどのように区分されるかを示します。selection-context を設定しない場合、その値は他の非ローカル オプションと同じようにグラフィカルな親になります。
すべてのグラフィック階層のトップレベルには View があり、これは GuiRangeSelectionContext を継承します。このトップレベルの View では、既定により selection-context 非ローカル オプションを self に設定します。したがって、下位のグラフィカル オブジェクトの selection-context が明示的に設定されていない場合はすべて同じ選択コンテキストに属することになります。

選択の変更

SelectionContext はそれぞれ Selection オブジェクトを持ちます。このオブジェクトには、コンテキストで現在選択されている項目が保持されています。何も選択されていない場合、Selection は空になります。SelectionContext.selection メンバ経由で選択にアクセスできます。選択を使用するアプリケーションでは、これを読み取り専用として扱い、将来の有効性に依存しないことを推奨します。むしろ、選択にアクセスしたら直ちにこれを使用して破棄するようにします。
SelectionContext は、以下の状況において現在の Selection を変更することができます。

イベントの発生

SelectionContext はさまざまな SelectionEvent を発生させて、Selection が変更したことを他のオブジェクトに通知します。
任意のウィンドウ (トップレベルの View) でアクティブになる SelectionContext は一度に 1 つだけで、アクティブな SelectionContext だけがその Selectionを変更できます。アクティブな選択コンテキストは、FocusManager.active-selection-context アクセサを介して ViewFocusManager に保持されます。
通常 SelectionContext.selection-context-request-activate 呼び出しを介して、SelectionContext は選択の操作に応答してアクティブになるように要求します。SelectionContext がアクティブでるかどうかは、SelectionContext.selection-context-active? 値を取得して確認できます。
SelectionContext 自体では次のイベントが発生します。

イベント クラス階層

SelectionContext で発生したイベントのイベント クラス階層を次に示します。
SelectionEvent





SelectionContextActivated SelectionChanged SelectionContextDeactivated

SelectionChanged




SelectableAdded SelectableRemoved

個別グラフィック選択のフレームワーク

個別グラフィック選択には、ユーザーがグラフィカル オブジェクトを選択したときに選択項目を見ることができるという便利な点があります。
たとえば、マウスのクリックまたは投げ縄ツールでグラフィカル オブジェクトを選択できるアプレットでは、DiscreteGraphicSelectionContext の機能を活用することができます。
DiscreteGraphicSelectionContextDiscreteSelectionを保持します。この選択は、順序付けられていない GraphicSelectable オブジェクトのリストで構成されています。ここで、DiscreteSelectionDiscreteSelection.items アクセッサによってアクセスされる Selectable オブジェクトで構成されていることに留意してください。Selectable のクラス階層は次のとおりです。
GuiEventTarget

Selectable

GraphicSelectable
この選択を保持するために、追加レベルのイベントが発生します。
グラフィカル オブジェクトが選択されたときの外観を変更するには、DiscreteGraphicSelectionContext.draw-style メンバを "invert"、"mask" または "none" に設定します。グラフィカル オブジェクトの背景のようなその他のプロパティを変更するには、draw-style を "none" に設定してから、SelectableAdded および SelectableRemoved イベントを待機するイベント ハンドラを個々の GraphicSelectable オブジェクトに追加します。

Selectable 内のループ

このセクションの例では、DiscreteSelection 内の GraphicSelectable オブジェクトをループする方法を示します。

例: Selectable 内のループ
{define-class FoodBook {inherits Frame}
  field author:#String
  field image:#any

  {constructor {default
                   name:String,
                   author:#String="",
                   image:#any=null,
                   ...}
    set self.name = name
    set self.author = author
    set self.image = image
    {construct-super self.image, width=1in, height=1in, ...}
  }
}

{let book1:FoodBook=
    {FoodBook
        "Heads-Up Appetite",
        author="M. Giraffe",
        image={image
                  source={url "curl://install/docs/default/images/leaves.jpg"}
              },
        graphic-selectable={GraphicSelectable}
    }
}

{let book2:FoodBook=
    {FoodBook
        "Feeding Near Your Feed",
        author="B. Goat",
        image={image
                  source={url "curl://install/docs/default/images/grass.jpg"}
              },
        graphic-selectable={GraphicSelectable}
    }
}

{let info-vbox:VBox={spaced-vbox}}

{let s-c-frame:DiscreteGraphicSelectionFrame=
    {DiscreteGraphicSelectionFrame
        {spaced-vbox
            margin=0.25cm,
            book1,
            book2
        },
        {on event:SelectionChanged at s-c:DiscreteGraphicSelectionFrame do
            {info-vbox.clear}
            {for s:GraphicSelectable in s-c.selection.items do
                {info-vbox.add (s.graphic asa FoodBook).author}
            }
        }
    }
}

{set s-c-frame.multiple-selection-enabled? = true}

{spaced-hbox
    valign="top",
    s-c-frame,
    {spaced-vbox
        {bold Leaves or Grass for Lunch?},
        {CommandButton label="Clear Selections",
            bound-command={s-c-frame.get-command "select-nothing"}
        },
        info-vbox




    }
}