メニュー

概要

メニューはユーザーがアプリケーションとやりとりをする主要な方法で、ユーザーがアプリケーションに対してコマンドを発することができる共通の便利なインターフェイスです。Curl は以下のような基本のメニュー スタイルを提供しています。
ユーザーは通常、マウスやその他のポインティング デバイスを使用してメニュー アイテムの選択や実行を行います。多くのメニュー システムはキーボードからのメニューへのアクセスもサポートしています。Curl は次のようなタイプのキーボードからのメニューへのアクセスをサポートしています。
次の表はメニューの構築に使用されるクラスとプロシージャを要約しています。
クラス / プロシージャ目的
MenuBar水平バー上にコンテンツを表示するメニューを作成します。MenuBarMenuItemSubMenu を含むことができ、SubMenu は追加の SubMenuMenuAction を含むことができます。MenuBar はプルダウン スタイルのメニューの作成に使用されます。
SubMenu追加の SubMenuMenuAction など他の MenuItem を含む MenuItem を作成します。右向き矢印がラベルの右端に描かれ、アイテムが SubMenu であることを示します。SubMenu を選択するとそのコンテンツが表示されます。SubMenu は関連付けられた MenuPaneSubMenu.cascade-pane プロパティでアクセス可能)上にコンテンツを表示します。
MenuPaneSubMenu または MenuAction といった MenuItem を含むメニューを作成します。MenuPane はポップアップ スタイルのメニューを作成し、SubMenu のコンテンツを表示するために使用されています。
DropdownMenuPaneコンテンツが使用可能な領域に収まらない場合に Scrollbar を提供する MenuPane を作成します。
MenuActionなんらかのアクションを行う MenuItem を作成します。MenuActionSubMenu 内または直接 MenuPane 内に入れることができます。MenuAction を直接 MenuBar の中に入れることができますが、これは一般的な使用法ではありません。
menu-separatorペイン内のグループ アイテムに使用される MenuPane にアイテムを分割する線を作成します。

メニュー アーキテクチャ

Curl のメニュー機能は CURL.GUI.BASECURL.GUI.CONTROLS の2つのパッケージに実装されています。CURL.GUI.BASE に含まれるクラスは、基本的な機能を実装しています。これらのパッケージには以下のものが含まれています。
ImplicitPointerGrabManager
MenuManager
Visual
Menu
BaseMenuBar
BaseMenuPane
MenuTraversal
HBox
MenuItem
SubMenu
MenuAction
BaseFrame
MenuSeparator
CURL.GUI.CONTROLS 内のクラスは Curl の外観のメカニズムを使用してメニューの外観を他のコントロールと調和させます。外観の作成についての詳細は、「カスタム コントロール UI の作成」を参照してください。
これらのクラスはユーザー インターフェイスを提供するメニュー システムのパーツを実装しています。これらのクラスは異なる外観に対し異なる UI オブジェクトを使用することができるようにする MultiUIControlFrame を継承しています。
MultiUIControlFrameBaseMenuBar
MenuBar
MultiUIControlFrameBaseMenuPane
MenuPane
DropdownMenuPane
これらのクラスはメニューの標準的なユーザー インターフェイス オブジェクトを実装しています。
FrameControlUI
MenuBarUI
SkinnableMenuBarUI
StandardMenuBarUI
MenuPaneUI
SkinnableMenuPaneUI
StandardMenuPaneUI

メニュー マネージャ

Curl のアプレット内のアクティブなメニューは MenuManager を持ち、これはオープン メニューの双方向リストである MenuManager.traversal を持っています。同時に1つの MenuManager だけがアクティブで、アクティブなメニューがない場合にはメニュー マネージャも存在しません。GuiManager は現在アクティブな MenuManager を追跡します。GuiManager.menu-manager プロパティでメニュー マネージャにアクセスすることができます。get-gui-manager プロシージャを使用して GuiManager にアクセスが可能です。
非ローカル オプション menu-manager によってアクティブなメニューのコンポーネントのメニュー マネージャにアクセスすることができます。

プルダウン メニュー

メニューの一般的なフォーマットの1つは、サブメニューのある水平 MenuBar で、さらにサブメニューとメニュー アクションを含んでいます。
次の例では、フレームを操作するメニューが含まれるオブジェクトが作成されています。MenuBar の「Change Text」のサブメニューはフレーム内のテキストのコンテンツを操作します。MenuBar 内の「Change color」のサブメニューはフレームの色を操作します。
この例では次の点に注意します。

例: イベント ハンドラを使用したプルダウンメニュー
{value
    let frame:Frame = 
        {Frame
            width = 3cm,
            height = 2cm,
            halign = "center",
            valign = "center",
            "Text"
        }
    let vbox:VBox = 
        {VBox
            background = "yellow",
            frame
        }
    let menubar:MenuBar = 
        {MenuBar
            {SubMenu label = "Change Text",
                {MenuAction
                    label = "apples",
                    {on Action do
                        {frame.add replace? = true, "apples"}
                    }
                },
                {MenuAction
                    label = "bread",
                    {on Action do
                        {frame.add replace? = true, "bread"}
                    }
                },
                {SubMenu
                    label = "More Options",
                    {MenuAction
                        label = "juice",
                        {on Action do
                            {frame.add replace? = true, "juice"}
                        }
                    },
                    {MenuAction
                        label = "milk",
                        {on Action do
                            {frame.add replace? = true, "milk"}
                        }
                    }
                }
            },
            {SubMenu label = "Change Color",
                {MenuAction label  = 
                    {Frame
                        background = "red",
                        width = 1cm,
                        height = 0.5cm},
                    {on Action do
                        set vbox.background = "red"}
                },
                {MenuAction label  = 
                    {Frame
                        background = "blue",
                        width = 1cm,
                        height = 0.5cm},
                    {on Action do
                        set vbox.background = "blue"}
                }
            }
        }
    {vbox.add before = frame, menubar}
    vbox
}
       

ポップアップ メニュー

2つめのメニューの一般的なフォーマットはサブメニュー、メニュー アクション、またはその両方を含む MenuPane です。MenuPane は多くの場合、ポップアップ メニューの実装に使われます。ポップアップ メニューの一般的なタイプはコンテキスト メニューで、呼び出されたコンテキストを反映したコンテンツを持っています。ユーザーは通常、プラットフォームで標準の動作(Windows では右クリック、Mac では cmdを押しながらクリック、マウスボタンが1つの Mac では ctrl を押しながらクリックなど)を通じてコンテキスト メニューを呼び出します。
Curl は次のようなポップアップ メニューの作成に特化したプロシージャを提供し、コンテキスト ポップアップ メニューとしてグラフィカル オブジェクトに連結します。
コンテキスト メニューは MenuPane で構成されおり、それは MenuAction または SubMenu を含む MenuAction で構成されています。いずれかのプロシージャを呼び出して、該当するイベント ハンドラを作成する際に、 MenuPane オブジェクトをキーワード引数として指定します。
次の例では context-popup を使用してコンテキスト メニューを Frame f1 に追加します(色はライム)。また add-context-popup を使用して f2 にコンテキスト メニューを追加します(色はオレンジ)。コンテキスト メニューによってフレームの色を変更することができます。コンテキスト メニューはそれぞれ3つの MenuAction オブジェクトを含む MenuPane で構成されています。

例: ポップアップ コンテキスト メニューを Frame に追加
{value
    let f1:Frame  = 
        {Frame
            width = 3cm,
            height = 2cm,
            background = "lime",
            {context-popup
                menu-pane = 
                    {MenuPane
                        {MenuAction label = "Lime",
                            {on Action do
                                set f1.background = "lime"}
                        },
                        {MenuAction label = "Magenta",
                            {on Action do
                                set f1.background = "magenta"}
                        },
                        {MenuAction label = "Aqua",
                            {on Action do
                                set f1.background = "aqua"}
                        }
                    }
            }
        }
    let f2:Frame  = 
        {Frame
            width = 3cm,
            height = 2cm,
            background = "orange"
        }
    {add-context-popup f2,
        menu-pane = 
            {MenuPane
                {MenuAction label = "Orange",
                    {on Action do
                        set f2.background = "orange"}
                },
                {MenuAction label = "Magenta",
                    {on Action do
                        set f2.background = "magenta"}
                },
                {MenuAction label = "Aqua",
                    {on Action do
                        set f2.background = "aqua"}
                }
            }
    }
    {spaced-hbox
        f1,
        f2
    }
}
次のような非ローカル オプションを使用してコンテキスト ポップアップ メニューを特定の型のオブジェクトに連結することもできます。
次の例は、これらの 3 つのオプションの使用状況を示します。Show View をクリックしてビューを見て、ビュー、テキスト フィールド、リンク上で右クリック(ボタンマウスが1つの Mac では ctrlを押しながらクリック)して、様々なコンテキスト メニューを見ます。
注意: ビューを破棄したら、例を元に戻してから再度実行して新しいビューを表示して他のアクションを実行してください。

例: context-menu-for-* options の使用法
{let msgtxt:String = "Revert example to get a new View"}
{let for-text-field:MenuPane  = 
    {MenuPane
        {MenuAction label = "Context menu for TextField"}
    }
}
{let for-link:MenuPane  = 
    {MenuPane
        {MenuAction label = "Context menu for link"}
    }
}
{let for-view:MenuPane  = 
    {MenuPane
        {MenuAction label = "Context menu for View"}
    }
}
{let v:View  = 
    {View
        title = "A View",
        context-menu-for-view = 
            {MenuPane
                {MenuAction label = "Context menu for View"}
            },
        {VBox
            height = 3cm,
            width = 5cm,
            {text This is a View.},
            {TextField value = "This is a text field.",
                context-menu-for-text-field = for-text-field
            },
            {paragraph
                Here is a link to the Curl web site.
                {link href = {url "http://www.curl.com"},
                context-menu-for-link = for-link,
                Curl Inc.
                }. It has a context menu.
            }
        }
    }
}
{HBox
    spacing = 3pt,
    {CommandButton label = "Show View",
        {on Action do
            {if (not v.destroyed?) then
                {v.show}
                 else
                    {popup-message msgtxt}
            }
        }
    },
    {CommandButton label = "Hide View",
        {on Action do
            {if (not v.destroyed?) then
                {v.hide}
                 else
                    {popup-message msgtxt}
            }
        }
    },
    {CommandButton label = "Destroy View",
        {on Action do
            {if (not v.destroyed?) then
                {v.destroy}
            }
        }
    }
}
MenuPane を使用して、コンテキスト メニュー以外にも大きなメニュー階層でサブメニューに関連付けられていないフライアウト メニューのようなメニューを作成することができます。次の例ではユーザーが Frame の背景の色を設定することができるようにするメニューを作成します。ユーザーは矢印のグラフィックをクリックしてメニューにアクセスします。
この例では DropdownMenuPane を使用し、これにより色の名前のリストにスクロール バーを指定しています。また、色のグループ化に menu-separator を使用しています。

例: フライアウト メニューの作成
{let frame1:Frame =
    {Frame
        width = 3cm,
        height = 1cm,
        background = "black"
    }
}
{define-proc {make-menu-action string:String}:MenuAction
    {return
        {MenuAction
            label = string,
            {on Action do
                set frame1.background = string
            }                              
        }
    }
}
{let colors:{Array-of String} = 
    {new {Array-of String},
        "pink",
        "lightsalmon",
        "khaki",
        "lightgreen",
        "skyblue",
        "orchid",
        "red",
        "orange",
        "yellow",
        "green",
        "blue",
        "purple",
        "gainsboro",
        "lightgray",
        "silver",
        "darkgray",
        "gray"
    }
}
{value
    let popup-menu:DropdownMenuPane = {DropdownMenuPane height = 4cm}
    {for string:String in colors do
        {popup-menu.add {make-menu-action string}}
    }
    {popup-menu.add {menu-separator}, index = 6}
    {popup-menu.add {menu-separator}, index = 13}
    let frame2:Frame =
        {Frame
            width = 8px,
            height = 15px,
            background = {url "../../default/images/large-arrow.gif"},
            {on PointerPress do
                {popup-menu.popup frame2, .6cm, 0cm}
            }
        }
    {spaced-vbox
        halign = "right",
        frame2,
        frame1
    }
}

Menu Opacity

MenuPane.view-opacity オプションは、メニュー ペインを表示するのに使われるビューの不透明度を設定します。ビューにおける一般的なセキュリティ制限のため、非特権アプレットで表示されるメニューは0.5 未満の不透明度を設定することは出来ません。View.set-opacity を参照してください。以下のサンプルでは不透明度が0.6のポップアップ メニューを作成します。このドキュメント・ビューワは、特権なしで実行されているので、不透明度が 0.5 未満であることを確認する為には、このサンプルをアプレットをして保存し、特権アプレットとして実行する必要があります。
メニューが装飾のないビューの中に表示されているので、装飾のないビューのセキュリティ制限が適用されます。View.defaultdecorations? のドキュメントを参照してください。

例: 半透明のメニュー ペイン
{value
    let f1:Frame  = 
        {Frame
            width = 3cm,
            height = 2cm,
            background = "lime",
            {context-popup
                menu-pane = 
                    {MenuPane
                        view-opacity = 0.6,
                        {MenuAction label = "Lime",
                            {on Action do
                                set f1.background = "lime"}
                        },
                        {MenuAction label = "Magenta",
                            {on Action do
                                set f1.background = "magenta"}
                        },
                        {MenuAction label = "Aqua",
                            {on Action do
                                set f1.background = "aqua"}
                        }
                    }
            }
        }
    let f2:Frame  = 
        {Frame
            width = 3cm,
            height = 2cm,
            background = "orange"
        }
    {add-context-popup f2,
        menu-pane = 
            {MenuPane
                view-opacity = 0.6,
                {MenuAction label = "Orange",
                    {on Action do
                        set f2.background = "orange"}
                },
                {MenuAction label = "Magenta",
                    {on Action do
                        set f2.background = "magenta"}
                },
                {MenuAction label = "Aqua",
                    {on Action do
                        set f2.background = "aqua"}
                }
            }
    }
    {spaced-hbox
        f1,
        f2
    }
}

View でのメニューの作成

アプレットが独立型アプレットとして実行されるときには、メニュー バーが一番よく使われます。「独立型アプレット」を参照してください。そのようなアプレットは View クラスを使用して、それぞれのメニュー バーを持つ別々のウィンドウを作成します。プラットフォームが Windows や Linux の場合、実行される独立型アプレットでは、通常アプリケーションのメイン メニュー バーはアプリケーション ウィンドウの上部に配置されています。プラットフォームが Mac の場合は、アプリケーション メニューは画面の上部にあります。Curl はアプリケーションのメニュー バーの指定に使用できる View.mac-menu-bar プロパティを提供しています。Macプラットフォーム はアプリケーションのメニュー バーでの非テキストのメニュー ラベルをサポートしていません。Mac のメニューの使用についての詳細は、「クロス プラットフォームのアプレットの作成」を参照してください。
次の例では、menubar と呼ばれるそれ自体のメニューで v と呼ばれる View を作成します。View の作成に関する詳細は、「ビュー」を参照してください。
この例に関して注意する点は以下の通りです。

例: メニュー バーを使用して新しい表示を作成
{value
    let frame:Frame  = 
        {Frame margin = 20pt,
            "Interior Text"
        }

    let vbox:VBox  = 
        {VBox background = "yellow",
            frame
        }
    let menubar:MenuBar  = 
        {MenuBar
            {SubMenu label = "Change Text",
                {MenuAction
                    label = "apples",
                    {on Action do
                        {frame.add replace? = true, "apples"}
                    }
                },
                {MenuAction
                    label = "bread",
                    {on Action do
                        {frame.add replace? = true, "bread"}
                    }
                },
                {menu-separator},
                {MenuAction
                    label = "juice",
                    {on Action do
                        {frame.add replace? = true, "juice"}
                    }
                },
                {MenuAction
                    label = "milk",
                    {on Action do
                        {frame.add replace? = true, "milk"}
                    }
                }
            }, 
            {SubMenu label = "Change color",
                {MenuAction 
                    label = "red",
                    {on Action do
                        set vbox.background = "red"
                    }
                },
                {MenuAction 
                    label = "blue",
                    {on Action do
                        set vbox.background = "blue"
                    }
                }
            }
        }
    {platform-switch
     case "win32" do
        {vbox.add before = frame, menubar}
     case "unix" do
        {vbox.add before = frame, menubar}
     case "linux" do
        {vbox.add before = frame, menubar}
     case "mac" do
        set v.mac-menu-bar = menubar
     else
        {error "Unknown platform!"}
    }
    let v:View = 
        {View
            vbox,
            {on e:WindowClose at v:View do
                {e.consume}  || don't let this event be re-fired
                {v.hide}     || just hide the view temporarily
            }
        }
    {CommandButton label = "Show new view",
        {on Action do
            {v.show}
        }
    }
}

動的メニュー

このセクションではアプレット内でのユーザーのアクションやその他のイベントに応じたメニューの変更方法について説明します。

メニュー アイテムを無効にする

ユーザーには使用できないメニュー アイテムを作成することにより、メニューがアプレットの状態を反映させなければならない場合があります。たとえば、ファイルが何も開かれていなければ、開かれたファイルを閉じるというアイテムは使用できません。ユーザーはこのアイテムの存在に気付いているので、このアイテムをメニューから削除したくはないでしょう。MenuItem.enabled? プロパティでメニュー アイテムを使用不能にして、現在関連付けられていないメニュー アイテムを無効にし、ユーザーがアクセスできるようにしたい時に有効にすることができます。
次の例では、「Edit」 メニューの 「Revert value」アイテムがテキスト フィールドの値が変更されるまで無効になっています。TextFieldValueChanged イベント ハンドラが初期値と現在値を比較して、それらが異なる場合に 「Revert value」メニュー アイテムを有効にします。「Revert value」メニュー アイテムの Action イベント ハンドラがテキスト フィールドの値を初期値に設定します。

例: メニュー アイテムを無効にする
{value
    let initial-value:String = "Initial value" 
    let revert:#MenuAction
    let text-field:TextField  = 
        {TextField value = initial-value,
            width = 4cm,
            {on ValueChanged do
                set revert.enabled? = (text-field.value != initial-value)
            }
        }

    let frame:Frame = 
        {Frame 
            margin = 12pt, 
            background = "yellow", 
            text-field
        }
    set revert =
        {MenuAction
            label = "Revert value",
            enabled? = false,
            {on Action do
                set text-field.value = initial-value
                set revert.enabled? = false
            }
        }
    {VBox
        {MenuBar
            {SubMenu 
                label = "Edit",
                {MenuAction
                    label = "Cut",
                    bound-command = {text-field.get-command "cut"}
                },
                {MenuAction
                    label = "Copy",
                    bound-command = {text-field.get-command "copy"}
                },
                {MenuAction
                    label = "Paste",
                    bound-command = {text-field.get-command "paste"}
                },
                revert
            }
        },
        frame
    }
}

メニュー アイテム ラベルの変更

MenuItem.label プロパティを設定することにより、アプリケーションの状態の変更に応じてメニュー アイテム のラベルを変更することができます。メニュー アクションやアプレットのほかの場所で起きたアクションのメニュー ラベルを設定することができます。
次の例では、メニュー アクションを使って、あるいはコマンド ボタンをクリックして、フレームの色を変更することができます。どちらの場合も、ボタンとメニュー アクション ラベルは現在のフレームの色を反映して変更されます。
次の例で注意する点を示します。

例: メニュー アイテム ラベルの変更
{value
    let pink:FillPattern  = "pink"
    let blue:FillPattern  = "skyblue"
    let frame:Frame  = 
        {Frame
            width = 3cm,
            height = 1cm,
            background = pink
        }

    let color-menu-action:MenuAction  = 
        {MenuAction
            label = "Make Blue"
        }
    let color-button:CommandButton = 
        {CommandButton
            width = {make-elastic},
            label = "Make Blue"
        }
    let color-event:EventHandler =
        {on Action do
            {if {frame.background.to-FillPattern} ==  pink then
                set frame.background = blue
                set color-menu-action.label = "Make Pink"
                set color-button.label = "Make Pink"
             else
                set frame.background = pink
                set color-menu-action.label = "Make Blue"
                set color-button.label = "Make Blue"
            }
        }
    {color-menu-action.add-event-handler color-event}
    {color-button.add-event-handler color-event}
    let menu:MenuBar  = 
        {MenuBar
            {SubMenu
                label = "Colors",
                color-menu-action
            }
        }

    {VBox
        menu,
        frame,
        color-button
    }
}

メニューを動的に構築

場合によっては、メニューにアイテムを追加したり、アイテムを削除する必要があります。これは addremoveclearなどのメソッドを使用するか、キーワード引数 menu-pane-procSubMenu の既定のコンストラクタに与えることにより行うことができます。
次の例では、アプレットがユーザーの選択に対応してサブメニューのコンテンツを変更しなければならないという状況をシミュレーションしています。プログラムによるメニューの変更に SubMenu.addSubMenu.clear メソッドを使用しています。サブメニュー select-colorFrame の背景の色を選択できるようにします。サブメニュー color-schemeselect-color メニューのどの色のグループを使えるかを選択できるようにします。color-scheme メニューのアイテムを選択する際は、イベント ハンドラが select-color メニューのすべてのアイテムの削除に select-color.clear を呼び出し、その後 make-menu-action で一連の新しいメニュー アクションを追加します。make-menu-action プロシージャは個々に指定された色の名前のメニュー アクションを作成しています。

例: add と clear を使用した動的なメニュー
{let frame:Frame = 
    {Frame
        width = 3cm,
        height = 2cm
    }
}
{let vbox:VBox = 
    {VBox
        background = "black",
        frame
    }
}
{define-proc {make-menu-action string:String}:MenuAction
    {return
        {MenuAction
            label = string,
            {on Action do
                set vbox.background = string
            }                              
        }
    }
}
{value
    let pastel:{Array-of String} = 
        {new {Array-of String},
            "pink",
            "lightsalmon",
            "khaki",
            "lightgreen",
            "skyblue",
            "orchid"
        }
    let bright:{Array-of String} = 
        {new {Array-of String},
            "red",
            "orange",
            "yellow",
            "green",
            "blue",
            "purple"
        }
    let gray:{Array-of String} = 
        {new {Array-of String},
            "gainsboro",
            "lightgray",
            "silver",
            "darkgray",
            "gray"
        }

    let select-color:SubMenu = 
        {SubMenu label = "Color"}
    let color-scheme:SubMenu = 
        {SubMenu 
            label = "Color Scheme",
            {MenuAction label = "Pastel",
                {on Action do
                    {select-color.clear}
                    {for s:String in pastel do
                        {select-color.add {make-menu-action s}}
                    } 
                }
            },
            {MenuAction label = "Bright",
                {on Action do
                    {select-color.clear}
                    {for s:String in bright do
                        {select-color.add {make-menu-action s}}
                    } 
                }
            },
            {MenuAction label = "Gray",
                {on Action do
                    {select-color.clear}
                    {for s:String in gray do
                        {select-color.add {make-menu-action s}}
                    } 
                }
            }
        }
    let menubar:MenuBar = {MenuBar}
    {for s:String in gray do
        {select-color.add {make-menu-action s}}
    }
    {menubar.add color-scheme}
    {menubar.add select-color}
    {vbox.add before = frame, menubar}
    {CommandButton
        label = "Show View",
        {on Action do
            let v:View = {View}
            {v.add vbox}
            {v.show}
        }
    }
}
次の例では、最近開かれたファイルのリストを保持するメニューをシミュレーションしています。例では実際にファイルを開かず、単にユーザーによって選択されたファイル名を取得します。また個々の選択されたファイル名をサブメニュー recent に追加しています。メニュー上のアイテムが4つを超える場合は、Menu.remove によって最初のアイテムを削除します。SubMenu.cascade-pane プロパティはサブメニューのアイテムを含むメニュー ペインを返します。
Menu.first-itemMenu.last-item プロパティはそれぞれ最初と最後のアイテムを返します。Menu.get メソッドは指定されたインデックスのアイテムを返します。

例: add と remove を使用した動的なメニュー
{value
    let files:int = 0    
    let frame:Frame = {Frame
                          width = 5cm,
                          height = 2cm,
                          halign = "center",
                          valign = "center"
                      }
    let vbox:VBox = {VBox
                        background = "khaki",
                        frame
                    }
    let recent:SubMenu = 
        {SubMenu
            label = "Recent Files"
        }
    let file-menu:SubMenu = 
        {SubMenu label = "File",
            {MenuAction 
                label = "Open",
                {on Action do
                    let file-url:#Url = 
                        {choose-file
                            style = "edit",
                            default-location = 
                                {url "../../default/support/connection.csv"}
                        }
                    {if-non-null file-url then
                        let file-name = file-url.pathname-tail
                        set files = files + 1
                        {recent.add 
                            {MenuAction
                                label = file-name,
                                {on Action do
                                    {frame.add replace? = true, file-name}
                                }
                            }
                        }
                        {if files > 4 then
                            set files = files - 1
                            {recent.cascade-pane.remove recent.cascade-pane.first-item}
                        }
                    }
                }
            },
            recent 
        }
    let menubar:MenuBar = {MenuBar}
    {menubar.add file-menu}
    {vbox.add before = frame, menubar}
    vbox
}
また、menu-pane-proc を使用してサブメニューに表示されるペインを動的に作成することができます。アプレットが Mac のプラットフォーム上で実行されることが想定され、サブメニューがアプリケーションのメニュー バーの子である場合、アプリケーションのメニュー バーとして作動している MenuBarBaseMenuBar.refresh メソッドを呼び出す必要があります。それを行わないと、変更はアプリケーション メニュー上で反映されません。

例: menu-pane-proc を使用した動的メニュー
{value 
    let pastel:{Array-of String} = 
        {new {Array-of String},
            "pink",
            "lightsalmon",
            "khaki",
            "lightgreen",
            "skyblue",
            "orchid"
        }
    let bright:{Array-of String} = 
        {new {Array-of String},
            "red",
            "orange",
            "yellow",
            "green",
            "blue",
            "purple"
        }
    let gray:{Array-of String} = 
        {new {Array-of String},
            "gainsboro",
            "lightgray",
            "silver",
            "darkgray",
            "gray"
        }
    let menu-values:{Array-of String} = gray
    let frame:Frame = 
        {Frame
            width = 3cm,
            height = 2cm
        }
    let vbox:VBox = 
        {VBox
            background = "black",
            frame
        }
    let select-color:SubMenu = 
        {SubMenu 
            label = "Color",
            menu-pane-proc =
                {proc {}:BaseMenuPane
                    let mp:MenuPane = {MenuPane}
                    || Create one MenuAction for each 
                    || string in the array
                    {for string in menu-values do
                        {mp.add
                            {MenuAction
                                label = string,
                                {on Action do
                                    set vbox.background = string
                                }                              
                            }
                        }
                    }
                    {return mp}
                }
        }
    let color-scheme:SubMenu = 
        {SubMenu 
            label = "Color Scheme",
            {MenuAction label = "Pastel",
                {on Action at m:MenuAction do
                    set menu-values = pastel
||--                        {change-color.menu-bar.refresh}  for Mac
                }
            },
            {MenuAction label = "Bright",
                {on Action do
                    set menu-values = bright
||--                        {change-color.menu-bar.refresh} for Mac
                }
            },
            {MenuAction label = "Gray",
                {on Action do
                    set menu-values = gray
||--                        {change-color.menu-bar.refresh} for Mac
                }
            }
        }
    let menubar:MenuBar = {MenuBar}
    {menubar.add color-scheme}
    {menubar.add select-color}
    {vbox.add before = frame, menubar}
    {CommandButton
        label = "Show View",
        {on Action do
            let v:View = {View}
            {v.add vbox}
            {v.show}
        }
    }
}

キーボード アクセラレータ

キーボード アクセラレータは、ユーザーがそれを押してメニューを開くことなくメニュー コマンドを実行することができるキーの組み合わせです。Curl 言語では、アクセラレータは常時 MenuAction に関連付けられていて MenuAction の作成時に key-accel-string キーワード引数で指定されます。
key-accel-string 引数はキーの組み合わせを表すString 値をとります。このキーの組み合わせは、最終的に KeyPressMatcher によって翻訳されます。KeyPressMatcher.default に関する API ドキュメンテーションではこの文字列の構造とコンテンツについて細かく説明しています。通常、文字列は1つ以上の修飾子 キーの識別子(Shift、Ctrl、Alt、Menu、または Accel)で構成されますが、それぞれプラスの記号(+)と最終の識別子が後に続きます。より複雑な文字列のフォーマットを使用して特定のプラットフォームに別々のアクセラレータ キーの組み合わせを指定することができます。
特別な Accel 修飾子は現在のプラットフォームの標準のアクセラレータ修飾子に関連付けられます。Mac のプラットフォームでは Menu(または Command)で、その他のプラットフォームでは Ctrl です。明示的に述べられる Ctrl や Menu の代わりにAccel を使用した場合、アクセラレータは他のプラットフォーム上でも期待通りに動作するでしょう。
アクセラレータの文字列は、現在のプラットフォームに適切な表示方法でメニューに表示される事がよくあります。Mac では、それ以外のすべてのプラットフォームで使われるテキストの名前の代わりに、グリフが修飾子に使われることがあります。
次の例では、key-accel-string のいくつかのバリエーションを説明します。最初に Accel 修飾子を使用する一般的なテクニックを示します。次に Mac のための異なるアクセラレータ キーの組み合わせをどのように明示的に指定するかを示します。3番目に複数のアクセラレータの組み合わせ方法を示します。

例: アクセラレータを使用して MenuAction を作成
{let text-space:Frame = 
    {Frame width = 5cm,
        height = 2cm,
        background = "khaki",
        valign = "center",
        halign = "center"
    }
}

{let menu-bar:MenuBar = 
    {MenuBar
        {SubMenu
            label = "File",
            {MenuAction 
                label = "Open",
                key-accel-string = "Accel+O",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Open"}
                }
            },
            {MenuAction
                label = "Print",
                key-accel-string = "Ctrl+P[mac:Menu+K]",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Print"}
                }
            },
            {MenuAction 
                label = "Save All",
                key-accel-string = "Accel+Shift+S",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Save All"}
                }
            }
        }
    }
}
{VBox
    menu-bar,
    text-space
}
既存のアクセラレータと同様のキーの組み合わせを備えるアクセラレータを追加する場合は、元のアクセラレータは削除されます。

メニュー アクセス キーとニーモニック

メニュー アクセス キーはユーザーがサブメニューを開いたり、メニュー のアクションをアクティブにしたりするときに押すキーのことです。メニュー アクセス キーにより、ユーザーはマウスの代わりにキーボードを使用してメニューの選択を行うことができます。ニーモニックとしてラベル内で下線を引かれた文字がユーザーにアクセス キーを知らせます。Macintosh のプラットフォームではメイン メニュー バー内でそのようなアクセス キーやニーモニックをサポートしておらず、Apple Human Interface Guidelines もそれらの使用を推奨していません。このため、Curl RTE はアプレットが Mac 上で実行されている場合はニーモニックを表示しません。
ラベル文字列内でアクセス キーに対応する文字の左にニーモニック マーカー文字、アンパサンド(&)を置くことで、アクセスキーを記述することが出来ます。 mark-mnemonicを参照してください。ニーモニック マーカー文字は、メニュー システムで文字の下線も示します。key-access パラメータを明示的に使用してアクセスキーを設定することもできます。文字をラベル文字列に表示したくない場合にこの方法は最も有用です。コントロールはキーボードショートカットを指定するために似たようなシステムを使用します。コントロールのキーボード ショートカット を参照して下さい。
ユーザーは以下のアクションを実行してアクセス キーを使用します。
メニュー内のアクセス キーは、ブラウザ内で実行されるアプレットよりも、独立したビュー内で実行されるデタッチド アプレットに適しています。ビューの中では1つのメニューだけが、 Alt-アクセス キー コンビネーションを認識することが出来ます。ブラウザが、メニューにアクセス キーを提供している場合は、どのメニューがアクセス キーを認識するか混乱する場合があります。

例: アクセス キーをサブメニューおよびメニュー アクションに連結
{let text-space:Frame = 
    {Frame width = 5cm,
        height = 2cm,
        background = "khaki",
        valign = "center",
        halign = "center"
    }
}
{let menu-bar:MenuBar = 
    {MenuBar
        {SubMenu
            label = "&File",
            {MenuAction 
                label = "&New",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected New"}
                }
            },
            {MenuAction 
                label = "&Open",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Open"}
                }
            },
            {MenuAction
                label = "&Print",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Print"}
                }
            },
            {MenuAction
                label = "E&xport",
                {on Action do
                    {text-space.clear}
                    {text-space.add "selected Export"}
                }
            }
        }
    }
}
{VBox
    menu-bar,
    text-space
}

メニュー ボタン

MenuButton でメニューを別のコントロール(通常は CommandButton)に関連付けることができます。次の例では、MenuButton で、最近開かれたファイルのリストを使用してファイルを開くことを実装します。「前の例」はサブメニューを使い同じ機能を実装しています。

例: 動的なメニューのコンテンツを持つ MenuButton
{value
    let files:int = 0
    let frame:Frame = 
        {Frame
            width = 5cm,
            height = 2cm,
            halign = "center",
            valign = "center"
        }
    let vbox:VBox = 
        {VBox
            background = "khaki",
            frame
        }
    let file-button:MenuButton = {MenuButton}
    let open-button:CommandButton = 
        {CommandButton
            label = "Open",
            {on Action do
                let file-url:#Url = 
                    {choose-file
                        style = "edit",
                        default-location = 
                            {url "../../default/support/connection.csv"}
                    }
                {if-non-null file-url then
                    let file-name = file-url.pathname-tail
                    set files = files + 1
                    {file-button.add 
                        {MenuAction
                            label = file-name,
                            {on Action do
                                {frame.add replace? = true, file-name}
                            }
                        }
                    }
                    {if files > 4 then
                        set files = files - 1
                        let g:Graphic = {file-button.menu-elements.read-one}
                        {file-button.remove g}
                    }
                }
            }
        }
    {file-button.add open-button}
    let menubar:MenuBar = {MenuBar}
    {menubar.add file-button}
    {vbox.add before = frame, menubar}
    vbox
}