Menus

The current interface does not support every possible operation you may want to do with a menu, but it can do the basics.

You create the menu tree in the menu function of your menu provider. Here, you typically use the WindowMenu::standard function to initialize menus with standard menu items. You then provide a Menu, or list of items, for each category of File, Edit, View, and Help. In a menu, you can manually specify a new button as follows.

#![allow(unused)]
fn main() {
fn menu(&self, env: &<Self::Environment as Environment>::Const, s: MSlock) -> WindowMenu {
    WindowMenu::standard(
        env,
        Menu::new("File")
            // here we are explicitly adding our own button
            // with its own custom command
            .push(MenuButton::new("New", "N", EventModifiers::default().set_command(), |_s| {
                println!("Clicked menu button");
            })),
        Menu::new("Edit"),
        Menu::new("View"),
        Menu::new("Help"),
        s
    )
}
}

The problem is that often times you do not explicitly know the action to take upfront, as it is dependent on the presence of some views in the view hierarchy. Hence, there is an alternate option as well.

The MenuChannel interface for menus also adheres to the sender and receiver pattern. It may be wise to visit the portals lesson first if you haven't already. The idea is that when you create a window, each menu item is really a receiver with no action in place. Then, the view hierarchy acts as the sender and provides the appropriate menu with an action. For instance, we could have a "Select All" menu item that initially has no action. Whenever a text field is visible, it enables the "Select All" menu item and populates it with an action to take when the menu item is clicked.

The MenuChannel is an object that acts as the coordinator between the sender and receiver. Initially, you create the menu channel object and pass it to the recipient menu and the view that will act as the sender. Note that MenuChannels are internally ARCed so that you can clone them without hassle.

Here's how it looks to pass the channel to the menu.

#![allow(unused)]
fn main() {
fn menu(&self, env: &<Self::Environment as Environment>::Const, s: MSlock) -> WindowMenu {
    // in practice, this initialization would be done
    // somewhere else (likely in environment initialization)
    // so that we can also give the channel to the views
    // but lets ignore that for sake of example
    let new_mc = MenuChannel::new();

    WindowMenu::standard(
        env,
        Menu::new("File")
            .push(MenuReceiver::new(&new_mc, "New", "N", EventModifiers::default().set_command(), s)),
        Menu::new("Edit"),
        Menu::new("View"),
        Menu::new("Help"),
        s
    )
}
}

In general, the easiest way to provide a sender is to use the menu_send modifier of any IVP. Whenever the target IVP is visible, it sends the specified action to the MenuReceiver. When it becomes hidden again, the corresponding action is unset.

#![allow(unused)]
fn main() {
// assume that we properly transferred the same channel as above
// to here
fn rabbits(new_mc: MenuChannel) {
  text("rabbits")
    // this will populate the appropriate menu receiver
    // with the given action
    // only whenever this IVP ("rabbits" label) is shown
    .menu_send(&new_mc, |_s| {
      println!("Creating new rabbit!");
    });
}
}

In some cases however, you may need more fine grained control. In these cases, you can explicitly use the set method of the MenuChannel. Make sure to call unset when the action is no longer needed.