Share

AgentMenus

What is an Agent watch?

It's a smartwatch, check out the website.

It can be programmed using the free Visual Studio 2012 Express and the .Net micro framework together with the Agent SDK

AgentMenus is a small framework for the Secretlabs Agent watch, that will do the boilerplate menu navigation stuff for you.

Get the sources at GitHub.

It has been designed to be easy and quick to set up, so you can focus on functionality instead of how to draw things on screen and handle the navigation buttons. I think the following screenshot shows it all (click for larger view):

screenshot with some arrows pointing out relations between code and result

Customizing and extending

In the demo there is an example of how you can use inheritance to customize a menu item. The example changes a menu item into a checkbox option:

public class CheckedMenuItem : MenuItem {

  public bool Checked;

  public override void Render(Bitmap display, Font font, int yPos) {
    int size = MenuHandler.rowHeight - 4;
    display.DrawRectangle(Color.White, 1, MenuHandler.xOffset, yPos + 2, size, size, 0, 0,
      Color.White, 0, 0, Color.White, Bitmap.MaxWidth, Bitmap.MaxHeight,
(ushort)(Checked ? 255 : 0)); display.DrawText(Text, font, Color.White, MenuHandler.xOffset + MenuHandler.rowHeight,
yPos + MenuHandler.yOffset); } public override void Select() { Checked = !Checked; // Toggle the checkbox MenuHandler.Refresh(); base.Select(); } }

In this example we inherit from MenuItem and override two methods. The Render method is customized to show the box at the left hand, and indent the text so it appears to the right of the box. If we had chosen to put the box on the right hand side, we could have done away with the display.DrawText line and simply call base.Render instead.

The 2nd method that we override is Select, where we toggle the checked state of the checkbox and have the display refreshed.

Adding non-menu screens

An app would typically not exist out of menu's only. The code behind menu item 7 shows how you can exit the menu and build your own screen. It also demonstrates how to get back into the menu.

private static bool doCustomStuff(MenuItem sender) {
  MenuHandler.Focus = false; // Enables the events handled below
  MenuHandler.Display.Clear();
  MenuHandler.Display.DrawText("View debug log", MenuHandler.font, Color.White, 5, 5);
  MenuHandler.Display.DrawText("for events", MenuHandler.font, Color.White, 5, 25);
  MenuHandler.Display.DrawText("Menu button", MenuHandler.font, Color.White, 5, 55);
  MenuHandler.Display.DrawText("returns to menu", MenuHandler.font, Color.White, 5, 75);
  MenuHandler.Display.Flush();
  MenuHandler.ButtonPress += MenuHandler_ButtonPress;
  return false;
}

static void MenuHandler_ButtonPress(Button button) {
  switch (button) {
    case Button.VK_SELECT: Debug.Print("Select"); break;
    case Button.VK_UP: Debug.Print("Up"); break;
    case Button.VK_DOWN: Debug.Print("Down"); break;
    case Button.VK_MENU: Debug.Print("Menu (exit)");
      MenuHandler.Focus = true;
      MenuHandler.Refresh();
      MenuHandler.ButtonPress -= MenuHandler_ButtonPress;
      break;
    default: Debug.Print("Unknown " + button.ToString()); break;
  }
}

The custom screenIt seems that subscribing to the hardware button events is not via standard (multicast) delegates. Only one subscription at a time is permitted. Actually this is to our advantage because otherwise all event handlers would have to check if their screen or mode is active.

This means you cannot subscribe to raw events yourself since the menu already has subscribed to them. Instead you have to subscribe to the MenuHandler.ButtonPress event which will pass on the button presses to which it is subscribed. It will only do so while MenuHandler.Focus is set to False.

The MenuHandler.ButtonPress event is a standard .Net multicast event, so if you have multiple custom screens behind menu-items you should be aware that all subscribed event handlers will receive the event. For this cause it might be a good practice to subscribe when entering and unsubscribe when leaving any custom screen.

Smaller menu using only part of the screen

Using part of the screen

An additional example has been added for using only part of the screen for the menu. In addition the demo shows how to assign a different (smaller) font to the menu and change the sizie of menuitems (offset and rowsize). This demo shows the time at the top and a gradient to the left, but this space should be utilised for status info applying to the domain of your custom app.

Disclaimer

I have not religiously followed encapsulation best practices. Considering the limited resources of the hardware I chose to go lean and take some shortcuts, for example using public fields instead of properties.