Immediate mode menu API

It would be nice if the menu had an immediate mode API rather than only the callback-based retained-mode one. So instead of doing this:

// Current API:

struct MyMenu {
  PDMenuItem *item_a;
  PDMenuItem *item_b;
  PDMenuItem *item_c;
};

void callback_a(void *state)
{
  // do thing A
}

static const char *items_b[] = { "Foo", "Bar" };

void callback_b(void *state_ptr)
{
  GameState *state = (GameState *)state_ptr;
  state->selected_b_index = pd->system->getMenuItemValue(state->menu.item_b);
  
  // do thing B
}

void menu_init(MyMenu *menu, GameState *state)
{
  menu->item_a = pd->system->addMenuItem("a", callback_a, state);
  menu->item_b = pd->system->addOptionsMenuItem("b", items_b, 2,
                                                callback_b, state);

  // ...
}

You could instead do this:

// Immediate mode API:

static const char *items_b[] = { "Foo", "Bar" };

void update_menu(GameState *state)
{
  if (pd->system->menuItem("a")) {
     // do thing A
  }
  if (pd->system->optionMenuItem("b", items_b, 2, &state->selected_b_index)) {
     // do thing B
  }
  // ...
}

This style of API not only means less code, but it also removes the duplication of the menu state between the playdate library and the user code. The code also gets easier and more debuggable due to not having callbacks.

The biggest practical advantage I see is being able to very easily present different menu choices depending on the state of the game:

// This option only makes sense while we're in a level
if (game_state->phase == Phase_InGame) {
  if (pd->system->menuItem("Quit Level"))
     quit_level();
} else if (game_state->phase == Phase_Cutscene) {
  if (pd->system->menuItem("Skip"))
     skip_cutscene();
}

This is much less convenient to achieve with the current API, as you need to remove and re-add the item(s) in the proper position.

Of course I’m not calling for replacing the current API, but it’d be nice to have both alternatives.

EDIT: if this is too big of a feature to add, it would at least be nice to have the option of adding a menu item in a specific position so one doesn’t need to remove and re-add all items if they want to change the first or second one; something like:

pd->system->addMenuItemAtPosition(int index, // ...same args as addMenuItem);

I can see the use case but imo it’s easy to implement any of that yourself on top of the existing API. I wouldn’t want to introduce redundant functionality into the SDK only so you can do things in a slightly different way if one could program their own light abstraction instead.

1 Like

Yeah, I guess it could be good to have as an external library for it but it’s probably not worth adding to the SDK at this point. Maybe worth considering for v.4.x :slight_smile:

In the end I can live just fine with the retained-mode API since I’m using it very little and I couldn’t justify the extra work in building and maintaining an extra library.