PrevNextUpHome SophiaFramework UNIVERSE 5.3

3.4. Menu

Menu(SFYMenu) is the responder to select an item from the menu list.

If the operation key or the ESCAPE key is pressed, default handler, which has already been registered into SFYMenu, will send the result event.

SFYMenu has function to close itself automatically after the specified time elapses.

There are three kinds of concrete menu classes: text menu[SFZTextMenu], grid menu[SFZGridMenu], and flex list menu[SFZFlexListMenu].

In this section, text menu(SFZTextMenu) will be described.

3.4.1. Text Menu

Let's make the text menu[SFZTextMenu] in the helloworld application class as the figure below.

Figure 3.24. Text menu

Text menu

In this code, color scheme or menu style will be changed when the item is selected from the menu list.

The following is the code to define function and structure which are useful to set the color scheme of text menu together at once.

Example 3.44. Define the helloworld application class and the custom color structure

// define the helloworld class
SFMTYPEDEFCLASS(helloworld)  //  macro to generate the useful types
class helloworld : public SFYApplication {

    SFMSEALCOPY(helloworld)  //  macro to prohibit the developer from copying this instance

private:

    MyWindowSmp _myWindow;

    // *** added and updated code segments are in bold

    // SFZTextMenu's smart pointer
    SFZTextMenuSmp _textMenu;

public:

    static SFCInvokerPtr Factory(Void);

private:

    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);

    // make MyWindow
    SFCError MakeMy(Void); // change name from Make into MakeMy

    // make SFZTextMenu
    SFCError MakeMenu(Void);

    // declare the function to set the text menu's color scheme together
    Void SetMenuColors(UserColorConstRef color);

    XANDLER_DECLARE_VOIDRENDER(OnRenderRequest) // declare the drawing handler
    XANDLER_DECLARE_BOOLEVENT(OnKey)            // declare the key handler 

    // declare the result handler of text menu
    XANDLER_DECLARE_VOIDRESULT(OnMenuResult)
};

// custom color structure(for setting menu's color scheme)
SFMTYPEDEFSTRUCT(UserColor)
struct UserColor {
    SFMTYPEDEFSTRUCT(AtomRec)
    struct AtomRec {

        // macro to declare the implicit type conversion from UserColor::AtomRec to UserColor
        SFMUTILITYATOMDECLARE(UserColor)

        SFXRGBColor::AtomRec light;
        SFXRGBColor::AtomRec base;
        SFXRGBColor::AtomRec dark;
        SFXRGBColor::AtomRec titleFore;
        SFXRGBColor::AtomRec titleBack;
        SFXRGBColor::AtomRec itemFore;
        SFXRGBColor::AtomRec itemBack;
        SFXRGBColor::AtomRec selFore;
        SFXRGBColor::AtomRec selBack;
    };
    SFXRGBColor light;
    SFXRGBColor base;
    SFXRGBColor dark;
    SFXRGBColor titleFore;
    SFXRGBColor titleBack;
    SFXRGBColor itemFore;
    SFXRGBColor itemBack;
    SFXRGBColor selFore;
    SFXRGBColor selBack;
};

// guarantee that sizes of two classes are same
SFMCONCEPTGLOBALTWO(concept_size_eq_type, UserColor, UserColor::AtomRec)

// this macro implements implicit type conversion from UserColor::AtomRec to UserColor
SFMUTILITYATOMIMPLEMENT(UserColor, AtomRec)
[Note] AtomRec struct

AtomRec struct is POD(Plain Old Data) struct in C language which has the same memory layout with the corresponding class or structure. For more details, see Optimize ARM RealView Compilation Tool for BREW(at Sophia Cradle web site).

If you define the AtomRec struct for the class or structure, you can optimize class or structure initialization with regard to size and speed. In this case, you must not use virtual function, multiple inheritance, and virtual inheritance for the class where the AtomRec struct is defined.

The AtomRec struct is used in the SFXEvent, SFXEventRange, and shape and color classes and so on.

In these classes, the atomic_cast operator is available for type conversion between SFXxxxxx and SFXxxxxx::AtomRec.

The atomic_cast operator is declared and implemented with the SFMUTILITYATOMICCASTDECLARE macro and the SFMUTILITYATOMICCASTIMPLEMENT macro.

The implicit type conversion operator from the AtomRec struct into class or structure is declared and implemented with the SFMUTILITYATOMDECLARE macro and the SFMUTILITYATOMIMPLEMENT macro.

Example 3.45. How to use

SFXRGBColor::AtomRec    atom  = {0x00, 0xFF, 0x00, 0x00};   // alpha, red, green, blue from left(note: alpha's position is different from SFXRGBColor)
SFXRGBColor             color = atom;                       // implicit type conversion
SFXRGBColor::AtomRecPtr ptr   = atomic_cast(&color);        // atomic_cast operator

The following is the code to make the text menu and implement its result handler, which will perform color scheme processing.

Example 3.46. 

// make the text menu
SFCError helloworld::MakeMenu(Void)
{
    SFCError error(SFERR_NO_ERROR);

    // create the text menu
    if ((_textMenu = SFZTextMenu::NewInstance(&error)) != null) {

        // set the text menu's parent responder to the application class(root)
        error = _textMenu->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // register the result handler into the text menu
            error = _textMenu->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnMenuResult)
            );

            if (error == SFERR_NO_ERROR) {

                // set title of the text menu
                error = _textMenu->SetTitle("TextMenu");

                if (error == SFERR_NO_ERROR) {

                    // intialize strings
                    SFXWideString item[] = {
                        "Elegant", "Carnival", "Dynamic", "Black", "***", "Scrollable Style", "Page Style", "***",
                        "Sample9", "Sample10", "Sample11", "Sample12", "Sample13", "Sample14", "Sample15", "Sample16"
                    };

                    // intialize the access key
                    // access keys are set at index from 0 to 8, and 15
                    AVKType key[lengthof(item)] = {
                        AVK_1, AVK_2, AVK_3, AVK_4, AVK_5, AVK_6, AVK_7, AVK_8,
                        AVK_9, 0,     0,     0,     0,     0,     0,     AVK_0
                    };

                    // intialize key icon
                    WChar icon[lengthof(item)] = {
                        '1', '2', '3', '4', '5', '6', '7', '8',
                        '9', ' ', ' ', ' ', ' ', ' ', ' ', '0'
                    };


                    for (SInt16 i = 0; i < lengthof(item); ++i) {

                        // add all items on the text menu together
                        if ((error = _textMenu->InsertLast(item[i], key[i], icon[i])) != SFERR_NO_ERROR) {

                            break;
                        }
                    }

                    // set the 5th item's enable flag to "false"
                    _textMenu->SetItemEnable(4, false);

                    // set the 8th otem's enable flag to "false"
                    _textMenu->SetItemEnable(7, false);

                    if (error == SFERR_NO_ERROR) {

                        // the LEFT key of the text menu is not used (key for sub menu)
                        _textMenu->SetSelectLeftKey(null);

                        // the RIGHT key of the text menu is not used (key for sub menu)
                        _textMenu->SetSelectRightKey(null);

                        // set the text menu's real region to device screen region
                        _textMenu->SetRealBound(GetLocalBound());

                        // set the text menu's state to "visible" + "active" + "enable" + "focus" together 
                        _textMenu->SetState(true, true, true, true);

                        // move text menu formost
                        _textMenu->ToFront();
                    }
                }
            }
        }
    }
    return error;
}

//  define colors
#define ATOM_MENU_ELEGANT_LIGHT             {0x00, 0xCB, 0xBD, 0xDC}
#define ATOM_MENU_ELEGANT_BASE              {0x00, 0xAB, 0xA7, 0xC4}
#define ATOM_MENU_ELEGANT_DARK              {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_ELEGANT_TITLE_BACK        {0x00, 0xBF, 0xE6, 0xCF}
#define ATOM_MENU_ELEGANT_TITLE_FORE        {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_ELEGANT_ITEM_FORE         {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_ELEGANT_ITEM_BACK         {0x00, 0xAB, 0xA7, 0xC4}
#define ATOM_MENU_ELEGANT_SELBACK           {0x00, 0xFC, 0xCD, 0xE5}
#define ATOM_MENU_ELEGANT_SELFORE           {0x00, 0xFF, 0x00, 0x00}
#define ATOM_MENU_CARNIVAL_LIGHT            {0x00, 0xFF, 0xCC, 0x00}
#define ATOM_MENU_CARNIVAL_BASE             {0x00, 0xFF, 0x7F, 0x00}
#define ATOM_MENU_CARNIVAL_DARK             {0x00, 0x0A, 0x50, 0xA1}
#define ATOM_MENU_CARNIVAL_TITLE_BACK       {0x00, 0x7D, 0x01, 0x77}
#define ATOM_MENU_CARNIVAL_TITLE_FORE       {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_CARNIVAL_ITEM_FORE        {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_CARNIVAL_ITEM_BACK        {0x00, 0xFF, 0x7F, 0x00}
#define ATOM_MENU_CARNIVAL_SELBACK          {0x00, 0xFF, 0xFF, 0x00}
#define ATOM_MENU_CARNIVAL_SELFORE          {0x00, 0x09, 0x30, 0x73}
#define ATOM_MENU_DYNAMIC_LIGHT             {0x00, 0xFF, 0xCC, 0x00}
#define ATOM_MENU_DYNAMIC_BASE              {0x00, 0x80, 0x9C, 0xC9}
#define ATOM_MENU_DYNAMIC_DARK              {0x00, 0x0A, 0x50, 0xA1}
#define ATOM_MENU_DYNAMIC_TITLE_BACK        {0x00, 0xFF, 0xFF, 0x00}
#define ATOM_MENU_DYNAMIC_TITLE_FORE        {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_ITEM_FORE         {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_ITEM_BACK         {0x00, 0x80, 0x9C, 0xC9}
#define ATOM_MENU_DYNAMIC_SELBACK           {0x00, 0xFF, 0x00, 0x00}
#define ATOM_MENU_DYNAMIC_SELFORE           {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_LIGHT                {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_BASE                 {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_DARK                 {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_TITLE_BACK           {0x00, 0x7F, 0x7F, 0x7F}
#define ATOM_MENU_MONO_TITLE_FORE           {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_ITEM_FORE            {0x00, 0xFF, 0xFF, 0xFF}
#define ATOM_MENU_MONO_ITEM_BACK            {0x00, 0x00, 0x00, 0x00}
#define ATOM_MENU_MONO_SELBACK              {0x00, 0x7F, 0x7F, 0x7F}
#define ATOM_MENU_MONO_SELFORE              {0x00, 0xFF, 0xFF, 0xFF}

// implement the result handler of the text menu
// this handler will be booted up when item is selected
XANDLER_IMPLEMENT_VOIDRESULT(helloworld, OnMenuResult, invoker, reason, result)
{
    // set the text menu's color scheme
    static UserColor::AtomRecConst color[] = {
        {
            ATOM_MENU_ELEGANT_LIGHT,        ATOM_MENU_ELEGANT_BASE,         ATOM_MENU_ELEGANT_DARK,
            ATOM_MENU_ELEGANT_TITLE_FORE,   ATOM_MENU_ELEGANT_TITLE_BACK,   ATOM_MENU_ELEGANT_ITEM_FORE,
            ATOM_MENU_ELEGANT_ITEM_BACK,    ATOM_MENU_ELEGANT_SELFORE,      ATOM_MENU_ELEGANT_SELBACK
        },
        {
            ATOM_MENU_CARNIVAL_LIGHT,       ATOM_MENU_CARNIVAL_BASE,        ATOM_MENU_CARNIVAL_DARK,
            ATOM_MENU_CARNIVAL_TITLE_FORE,  ATOM_MENU_CARNIVAL_TITLE_BACK,  ATOM_MENU_CARNIVAL_ITEM_FORE,
            ATOM_MENU_CARNIVAL_ITEM_BACK,   ATOM_MENU_CARNIVAL_SELFORE,     ATOM_MENU_CARNIVAL_SELBACK
        },
        {
            ATOM_MENU_DYNAMIC_LIGHT,        ATOM_MENU_DYNAMIC_BASE,         ATOM_MENU_DYNAMIC_DARK,
            ATOM_MENU_DYNAMIC_TITLE_FORE,   ATOM_MENU_DYNAMIC_TITLE_BACK,   ATOM_MENU_DYNAMIC_ITEM_FORE,
            ATOM_MENU_DYNAMIC_ITEM_BACK,    ATOM_MENU_DYNAMIC_SELFORE,      ATOM_MENU_DYNAMIC_SELBACK
        },
        {
            ATOM_MENU_MONO_LIGHT,           ATOM_MENU_MONO_BASE,            ATOM_MENU_MONO_DARK,
            ATOM_MENU_MONO_TITLE_FORE,      ATOM_MENU_MONO_TITLE_BACK,      ATOM_MENU_MONO_ITEM_FORE,
            ATOM_MENU_MONO_ITEM_BACK,       ATOM_MENU_MONO_SELFORE,         ATOM_MENU_MONO_SELBACK
        }
    };

    switch (reason) {
        case SFP16_RESULT_OK:       // when item is selected

            // display the selected item's string on BREW Output Window
            TRACE("'%S' is selected", _textMenu->GetItemText(static_cast<SInt16>(result)).GetCString());

            switch (result) {
                case 0:
                case 1:
                case 2:
                case 3:

                    // change the color scheme of the text menu according to the item number
                    SetMenuColors(color[result]);

                    break;
                case 5:

                    // change the menu style to the scroll style
                    _textMenu->SetMenuStyle(SFZTextMenu::SCROLLABLE_STYLE);

                    break;
                case 6:

                    // change the menu style to the page style
                    _textMenu->SetMenuStyle(SFZTextMenu::PAGE_STYLE);

                    break;
            }
            break;
        case SFP16_RESULT_ESCAPE:   // when the ESCAPE key(by default, the CLEAR key) is pressed

            // close the text menu
            invoker->Terminate();

            break;
    }
    return;
}

// set the text menu's color together
Void helloworld::SetMenuColors(UserColorConstRef color)
{
    if (_textMenu != null) {

        // set menu title's foreground color
        _textMenu->SetTitleForeColor(color.titleFore);

        // set menu title's background color
        _textMenu->SetTitleBackColor(color.titleBack);

        // set selected item's background color
        _textMenu->SetSelBackColor(color.selBack);

        // set selected item's foreground color
        _textMenu->SetSelForeColor(color.selFore);

        // set menu's bevel color
        _textMenu->SetBevelColor(SFXBevelColor(color.light, color.base, color.dark));

        // set foreground color and background color for all items
        for (SInt16 i = 0; i < _textMenu->GetItemCount(); ++i) {
            _textMenu->SetItemForeColor(i, color.itemFore);
            _textMenu->SetItemBackColor(i, color.itemBack);
        }

    }
    return;
}
[Tip] Parent responder of the menu

In general, the parent responder of the menu is set to the application class [i.e., root(SFZRoot) which the helloworld application class(SFYApplication) contains by default].

The root can be obtained by calling SFYApplication::GetRoot().

[Note] Register the result handler into the menu

The menu will receive the result event when the item is selected.

You can register the result handler for the result event into the text menu with the SFYResponder::RegisterHandler function.

Reference: Result Event[SFEVT_RESPONDER_RESULT] | Handler for the Result Event[XANDLER_DECLARE_VOIDRESULT]

[Note] Access key

The function of access key is that user can select a particular item directly by pressing the corresponding key once, which is associated with that item.

Let's update the code to display the text menu when the "2" key is pressed in the initial screen.

Example 3.47. Update drawing processing and key handling in the initial screen

// drawing handler
XANDLER_IMPLEMENT_VOIDRENDER(helloworld, OnRenderRequest, invoker, reason, graphics)
{
    unused(invoker);

    // by default, background will be filled in white color

    // *** updated code segments are in bold

    graphics->DrawSingleText("Key 1- Controls", SFXGrid(20, 20), GetLocalBound(), COLOR_BLACK);
    graphics->DrawSingleText("Key 2- TextMenu", SFXGrid(20, 40), GetLocalBound(), COLOR_BLACK);

    return;
}

// key handler 
XANDLER_IMPLEMENT_BOOLEVENT(helloworld, OnKey, invoker, event)
{
    unused(invoker);

    // *** added code segments are in bold

    // handle the key event only when the application class has no visible child responder
    if (this->GetChildFront(true, false, false, false) == null) {

        switch (event.GetP16()) {
            case AVK_SELECT:
                Terminate();
                return true;
            case AVK_1:
                TRACE("key 1");
                if (MakeMy() != SFERR_NO_ERROR) {
                    return false;
                }
                return true;
            case AVK_2: // when the "2" key is pressed
                TRACE("key 2");

                // make the text menu
                if (MakeMenu() != SFERR_NO_ERROR) {
                    return false;
                }

                // return "true" since the key event handled
                return true;
        }
    }
    // return "false" when key event is not handled
    return false;
}

As a result of added code segments in bold,

    ...

    // handle the key event only when the application class has no visible child responder
    if (this->GetChildFront(true, false, false, false) == null) {
    ...

the helloworld application class(i.e., root[SFZRoot]) will not handle the key events when it has the child responder whose visible state is "ON"(i.e., when MyWindow or _textMenu is displayed).

With the added code segments in bold above, key events which are not handled by MyWindow nor _textMenu will be handled by the helloworld application class(i.e., root[SFZRoot]).

The following is execution result on BREW simulator.

  1. The text menu(textMenu) will be displayed when the "2" key is pressed.
  2. To select an item from text menu(textMenu), move the focus among items with the selection keys and then press the operation key, or press the access key directly.
[Note] Selection key of the text menu

The focus can be moved among items with the selection key of the text menu.

In default settings, the focus will be moved the previous or the next item if the UP or DOWN key is pressed respectively.

Setting of the selection key can be changed with the SFYMenu::SetSelectUpKey and SFYMenu::SetSelectDownKey functions.

[Note] Operation key of the text menu

An item can be selected with the operation key of menu.

In default settings, the focused item will be selected if the SELECT key is pressed.

Setting of the operation key can be changed with the SFYMenu::SetOperateKey function.

Reference: Abstract Class that Represents a Menu[SFYMenu]

Figure 3.25. Initial screen (helloworld)

Initial screen (helloworld)

Figure 3.26. Text menu(Various Screen Example)

Text menu(Various Screen Example)

Figure 3.27. BREW Output Window when the item is selected

BREW Output Window when the item is selected

Reference: Menu(Basic) | SFYResponder::GetChildFront