PrevNextUpHome SophiaFramework UNIVERSE 5.3

3.7. Control(Applied)

3.7.1. Flex List Box Control

Let's make the flex list box control(SFZFlexListBoxControl) in the previous custom container as the figure below.

Figure 3.32. Flex list box control

Flex list box control
[Note] Flex list box control

Flex list box control(SFZFlexListBoxControl) is the control designed to select an item among the item list.

It has more advanced function such as handling the image than SFZListBoxControl.

For more details, see SFZFlexListBoxControl.

The following is the code to make the flex list box control in "CustomContainer".

Example 3.54. Make the flex list box control

// define the CustomContainer class
SFMTYPEDEFRESPONDER(CustomContainer)  //  macro to generate the useful types
class CustomContainer : public SFYContainer
{
    SFMSEALRESPONDER(CustomContainer)  //  macro to prohibit the developer from copying this instance

    // macro to specify the inheritance relation from SFYResponder to this class
    SFMRESPONDERINSTANTIATETHREE(CustomContainer, SFYContainer, SFYWidget, SFYResponder)

public:

    // since 4-character type of small alphabet and symbol is reserved 
    // define ('C', 'C', 'T', 'N') of capital alphabets for CustomContainer type
    enum CodeEnum {
        CODE_TYPE = four_char_code('C', 'C', 'T', 'N')
    };
    SFMTYPEDEFTYPE(CodeEnum)

public:

    static CustomContainerSmp NewInstance(SFCErrorPtr exception = null);

    // *** added code segments are in bold

    // make flex list box control
    SFCError Make(Void);

protected:

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

    // drawing handler
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const;

private:

    // result handler of the flex list box control
    XANDLER_DECLARE_VOIDRESULT(OnListResult)
};

// make flex list box control
SFCError CustomContainer::Make(Void)
{
    SFZFlexListBoxControlSmp list;
    SFZFlexListBoxControl::ItemRec item;
    SFBShellSmp shell(SFBShell::GetInstance());
    SFBImageSmp normal(shell->LoadResImage("helloworld.bar", IDI_OBJECT_5001));
    SFBImageSmp select(shell->LoadResImage("helloworld.bar", IDI_OBJECT_5002));
    SFCError error(SFERR_NO_ERROR);

    // create the flex list box control
    if ((list = SFZFlexListBoxControl::NewInstance(&error)) != null) {

        // set the flex list box control's parent responder to the container
        if ((error = list->SetParent(GetThis())) == SFERR_NO_ERROR) {

            // register the result handler into flex list box control
            error = list->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnListResult)
            );

            if (error == SFERR_NO_ERROR) {

                // set the background color of even numbered items
                list->SetEvenListColor(SFXRGBColor(0xFF, 0xFF, 0xCC, 0x00));

                // set the background color of odd numbered items
                list->SetOddListColor(SFXRGBColor(0xFF, 0xCC, 0xFF, 0x00));

                // set the default horizontal alignment to "center-aligned"
                list->SetHorizontalAlign(SFZFlexListBoxControl::NORMAL, SFZFlexListBoxControl::HORIZONTAL_CENTER);

                // set repeat-scroll flag to "false"
                list->SetScrollRepeat(false);

                // set the scroll bar's width to 5
                list->SetScrollBarWidth(5);

                // set that multiple string is displayed when selected
                list->SetToggle(SFZFlexListBoxControl::TOGGLE_MULTIPLE_BODY_TEXT);

                // set the flex list box control's move-item-up key to LEFT key
                list->SetScrollUpKey(AVK_LEFT);

                // set the flex list box control's move-item-down key to RIGHT key
                list->SetScrollDownKey(AVK_RIGHT);

                // set the enable flag of access key to "true" (enabled)
                list->SetAccessKeyEnable(true);

                // append an item with the item structure
                // though only string can be appended,
                // with the item structure is recommended to append an image

                // set the enable flag of the item structure to "true"
                item.property.enable = true;

                // set the access key and the command ID of the item structure
                // (ID is not used in this case)
                item.property.key = 0;
                item.property.id = SFZFlexListBoxControl::INVALID_COMMAND_ID;

                // set the image of the item structure
                item.normal.body.icon = normal;
                item.select.body.icon = select;

                // append 10 items
                for (SInt32 i = 0;  i < 10; ++i) {

                    // set the text of the item structure
                    item.normal.header.text.Set(SFXWideString::Format("%02d", i));
                    item.normal.body.text.Set(SFXWideString::Format("List item %02d", i));
                    item.select.body.text.Set(SFXWideString::Format("The item %02d is selected.", i));

                    // set the access key of the item structure
                    item.property.key = AVK_0 + i;

                    // insert the item at the tail
                    if ((error = list->InsertLast(item)) != SFERR_NO_ERROR) {
                        break;
                    }
                }

                if (error == SFERR_NO_ERROR) {

                    // set the flex list box control's real region
                    list->SetRealBound(list->GetSuitableBound(GetLocalBound().Deflate(10, 10)).SnapCenter(GetLocalBound().GetCenter()));

                    // set the flex list box control's state to "visible" + "active" + "enable" + "focus" together 
                    list->SetState(true, true, true, true);

                    list->SetCurrentValue(1);

                    // (*)
                    // set the current value after setting the enable flag to "true"
                    //
                }
            }
        }
    }
    return error;
}

In default settings of SFZFlexListBoxControl, the SFEVT_KEY event of the CLEAR key will be handled by SFZFlexListBoxControl and the result event of [SFXEvent(SFEVT_RESPONDER_RESULT, SFP16_RESULT_ESCAPE, 0)] will be received when the CLEAR key is pressed.

As a result, the SFEVT_KEY event of the CLEAR key will be handled before distributed to ItsWindow. And the window will not be terminated in the current key handler of ItsWindow.

Therefore, we have to implement the result handler of SFZFlexListBoxControl to send the SFEVT_KEY event of the CLEAR key to the parent responder(ItsWindow).

Example 3.55. Implement the result handler

// result handler of the flex list box control
XANDLER_IMPLEMENT_VOIDRESULT(CustomContainer, OnListResult, invoker, reason, result)
{
    SFYResponderSmp parent;

    unused(invoker);
    unused(result);
    switch (reason) {

        // the result event will be received when the CLEAR key is pressed
        case SFP16_RESULT_ESCAPE:

            if ((parent = GetParent()) != null) {

                // send the CLEAR key event to the container's parent responder(ItsWindow)
                // * key handler of the container's parent responder(ItsWindow) will be booted up from foreground to background(in the reverse order of registration)
                parent->InvokeForward(SFXEvent(SFEVT_KEY, AVK_CLR, 0), false);

            }
            break;
    }
    return;
}
[Note] SFYResponder::InvokeBackward / SFYResponder::InvokeForward function

To send an event to the specific responder in the callback type, use the SFYResponder::InvokeBackward / SFYResponder::InvokeForward function. Different from the key event, this event will not be distributred to child responders of target responder.

To distribute an event to the child responders of the target responder, use the SFYResponder::Distribute function. In this case, it is necessary to register the dispatching rule into the tracer.

In the code below, after the custom container "CustomContainer" is made, the flex list box control will be created and placed there.

Example 3.56. Update the ItsWindow class

// update so that ItsWindow calls container's Make function
SFCError ItsWindow::Make(Void)
{
    CustomContainerSmp container;
    SFZContainerScrollBarControlSmp bar;
    SFCError error(SFERR_NO_ERROR);

    // create the custom container
    if ((container = CustomContainer::NewInstance(&error)) != null) {

        // set the custom container's parent responder to ItsWindow
        if ((error = container->SetParent(GetThis())) == SFERR_NO_ERROR) {

            // ...(omitted)...

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

            // make the flex list box control in the custom container
            container->Make();
        }
    }
    if (error == SFERR_NO_ERROR) {

        // create the scroll bar control only for the container
        if ((bar = SFZContainerScrollBarControl::NewInstance(&error)) != null) {

            // ...(omitted)...

        }
    }
    return error;
}

3.7.2. Tab Control and Tab Page

Let's make the tab control(SFZTabControl) and tab pages(SFZTabPage) as the figure below.

Figure 3.33. Tab control and tab page

Tab control and tab page
[Note] Tab control and tab page

Tab control[SFZTabControl] is the control designed to manage and display the tab page(container for the tab control), its child responder, in the tabbed form.

The tab part of the tab control, called "tab label", and the corresponding tab page(SFZTabPage) will behave or be drawn differently depending on the state of the tab page. For more details, see SFYTabControl.

SFZTabPage is the container responder with a title image/string and a hint string, and has almost the same functionality with the general purpose container(SFZContainer).

* In general, though the control does not have the container as the child responder, the tab control has the container as the tab page exceptionally.

In the code below, the responders, MyWindow and CustomContainer, are used as tab pages by inheriting from SFZTabPage.

The following is the code to change the parent class of MyWindow and CustomContainer for SFZTabPage.

Example 3.57. Change the parent class of MyWindow and CustomContainer for SFZTabPage


// *** updated code segments are in bold

// define the MyWindow class
SFMTYPEDEFRESPONDER(MyWindow)  //  macro to generate the useful types
class MyWindow : public SFZTabPage {

    SFMSEALRESPONDER(MyWindow)  //  macro to prohibit the developer from copying this instance

    // macro to specify the inheritance relation from SFYResponder to this class
    SFMRESPONDERINSTANTIATEFOUR(MyWindow, SFZTabPage, SFYContainer, SFYWidget, SFYResponder)

    // ... (omitted) ...

};

// define the CustomContainer calss
SFMTYPEDEFRESPONDER(CustomContainer)  //  macro to generate the useful types
class CustomContainer : public SFZTabPage {

    SFMSEALRESPONDER(CustomContainer)  //  macro to prohibit the developer from copying this instance

    // macro to specify the inheritance relation from SFYResponder to this class
    SFMRESPONDERINSTANTIATEFOUR(CustomContainer, SFZTabPage, SFYContainer, SFYWidget, SFYResponder)

    // ... (omitted) ...

};

Next, let's implement the window(TabWindow) to make the tab control.

Example 3.58. Implement TabWindow

// define TabWindow class
SFMTYPEDEFRESPONDER(TabWindow)
class TabWindow : public SFZWindow {

    SFMSEALRESPONDER(TabWindow)  //  macro to prohibit the developer from copying this instance

    // macro to specify the inheritance relation from SFYResponder to this class
    SFMRESPONDERINSTANTIATEFOUR(TabWindow, SFZWindow, SFYContainer, SFYWidget, SFYResponder)

public:

    // since 4-character type of small alphabet and symbol is reserved 
    // define ('T', 'A', 'B', 'W') of capital alphabets for TabWindow type
    enum CodeEnum {
        CODE_TYPE = four_char_code('T', 'A', 'B', 'W')
    };
    SFMTYPEDEFTYPE(CodeEnum)

private:

    // declare the  tab control
    SFZTabControlSmp _tab;

public:

    static TabWindowSmp NewInstance(SFCErrorPtr exception = null);

    // make the tab control and the tab page
    SFCError Make();

protected:

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

private:

    // key handler 
    XANDLER_DECLARE_BOOLEVENT(OnKey)

};

// constructor
TabWindow::TabWindow(Void) static_throws
{
    if (static_try()) {

        // set the responder type
        SetType(CODE_TYPE);

        // register the key handler in TabWindow
        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
            XANDLER_INTERNAL(OnKey)
        ));
    }
}

// destructor
TabWindow::~TabWindow(Void)
{
}

// create TabWindow
TabWindowSmp TabWindow::NewInstance(SFCErrorPtr exception)
{
    return static_pointer_cast<TabWindow>(Factory(:: new TabWindow, exception));
}

#define COLOR_TAB COLOR_WHITE // tab control's background color 

// make tab control and tab page on TabWindow
SFCError TabWindow::Make()
{
    MyWindowSmp myWindow;
    CustomContainerSmp customContainer;
    SFCError error(SFERR_NO_ERROR);

    // create the tab control
    if ((_tab = SFZTabControl::NewInstance(&error)) != null) {

        // set the tab control's parent responder to TabWindow
        error = _tab->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // set the tab control's real region
            _tab->SetRealBound(GetLocalBound());

            // set the colors of the various tab control's parts
            _tab->SetBackgroundColor(COLOR_TAB);

            // draw the tab control's border line
            _tab->SetDrawBorder(true);

            // undisplay the tab control's scroll bar
            _tab->SetScrollBarWidth(0);
            _tab->SetScrollBarControl(SFYScrollBarControlSmp::EmptyInstance());

            // set the maximum tabs displayed in the tab control to 2 page
            _tab->SetFieldValue(2);

            // set the tab control's state to "visible" + "active" + "enable" + "focus" together
            _tab->SetState(true, true, true, true);

            // make MyWindow(MyWindow's inheriting from parent responder is changed into SFZTabPage)
            if ((myWindow = MyWindow::NewInstance(&error)) != null) {

                // set the MyWindow's parent responder to the tab control
                // MyWindow's real region is automatically set to the region obtained by excluding tab label and hint region from tab control region
                error = myWindow->SetParent(_tab);

                if (error == SFERR_NO_ERROR) {

                    // set the title text of MyWindow as tab page (this text will be dispalyed in the tab region)
                    myWindow->SetTitle("Controls");

                    // set the hint text of MyWindow as tab page
                    myWindow->SetHint("This is MyWindow");

                    // set the MyWindow's state to "visible" + "active" + "enable" + "unfocus" together 
                    // * this statement can be omitted since it is default setting
                    myWindow->SetState(true, true, true, false);

                    // make responders in MyWindow
                    myWindow->Make();

                    // make CustomContainer(CustomContainer's inheriting from the parent responder has been changed into SFZTabPage)
                    if ((customContainer = CustomContainer::NewInstance(&error)) != null) {

                        error = customContainer->SetParent(_tab);

                        if (error == SFERR_NO_ERROR) {

                            // set the title text of CustomContainer as tab page (this text will be displayed in tab region)
                            customContainer->SetTitle("Container");

                            // set the hint text of CustomContainer as tab page
                            customContainer->SetHint("This is CustomContainer");

                            // set CustomContainer's state to "visible" + "active" + "enable" + "unfocus" together 
                            // * this statement can be omitted since it is default setting
                            customContainer->SetState(true, true, true, false);

                            // make responders in CustomContainer
                            customContainer->Make();

                            // calculate the suitable virtual region on all tab pages automatically
                            _tab->AdjustPages();

                            // select the 2nd tab page(CustomContainer)
                            _tab->FocusPage(1);
                        }
                    }
                }
            }
        }
    }
    return error;
}

// key handler
XANDLER_IMPLEMENT_BOOLEVENT(TabWindow, OnKey, invoker, event)
{
    Bool result(false);

    unused(invoker);
    switch (event.GetP16()) {

        case AVK_CLR: // when the CLEAR key is pressed

            // terminate TabWindow
            Terminate();

            result = true;
            break;
    }
    return result;
}
[Tip] The order to boot up the key handlers

If more than one key handler are registered into the same responder, the key handlers will be booted up in the reverse order of registration.

Reference: Key Event[from SFEVT_KEY to SFEVT_KEY_HOOK_RELEASE]

[Note] Settings of the real region of the tab page

In the tab page, when the parent responder is set to the tab control, its real region is automatically calculated and set at the same time. Therefore, there is no need to set the real region of the tab page.

In the above example, by executing the _myWindow->SetParent(_tab); statement, the parent responder of _myWindow is set to _tab and the _myWindow's real region is set to the region obtained by excluding the tab region from the _tab's real region.

[Note] Setting of the state of the tab page

Immediately after creating the tab page(SFZTabPage instance), its state is automatically set to "visible" + "active" + "enable" + "unfocus" together as an initial value.

In almost all cases, since this setting is enough, you don't have to set the tab page's state.

The following is the code to display TabWindow when the "4" key is pressed in the initial screen of the helloworld applet.

Example 3.59. Update the helloworld application class

// 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;
    ItsWindowSmp _itsWindow;
    SFZTextMenuSmp _textMenu;

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

    // TabWindow's smart pointer
    TabWindowSmp _tabWindow;

public:

    static SFCInvokerPtr Factory(Void);

private:

    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);
    SFCError MakeMy(Void);
    SFCError MakeMenu(Void);
    SFCError MakeIts(Void);

    // make TabWindow
    SFCError MakeTab(Void);

    // ...(omitted)...
};

// make TabWindow
SFCError helloworld::MakeTab(Void)
{
    SFCError error(SFERR_NO_ERROR);

    // create TabWindow
    if ((_tabWindow = TabWindow::NewInstance(&error)) != null) {

        // 
        error = _tabWindow->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // set TabWindow's real region to device screen region
            _tabWindow->SetRealBound(GetLocalBound());

            // set TabWindow's state to "visible" + "active" + "enable" + "focus" together
            _tabWindow->SetState(true, true, true, true);

            // make tab control, tab page, etc.
            _tabWindow->Make();

            // move TabWindow foremost
            _tabWindow->ToFront();
        }
    }
    return error;
}

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

    // responder's background is filled in the color set with SFYWidget::SetBackgroundColor()[default: white]

    // draw text
    graphics->DrawSingleText("Key 1 - Controls", SFXGrid(20, 20), GetLocalBound(), COLOR_BLACK);
    graphics->DrawSingleText("Key 2 - TextMenu", SFXGrid(20, 40), GetLocalBound(), COLOR_BLACK);
    graphics->DrawSingleText("Key 3 - Container", SFXGrid(20, 60), GetLocalBound(), COLOR_BLACK);

    graphics->DrawSingleText("Key 4 - TabControl", SFXGrid(20, 80), GetLocalBound(), COLOR_BLACK);

    return;
}

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

    // handle the key events when the application class has no visible child responder
    if (this->GetChildFront(true, false, false, false) == null) {
        switch (event.GetP16()) {
            case AVK_SELECT:
                Terminate();
                result = true;
                break;
            case AVK_1:
                TRACE("key 1");
                if (MakeMy() == SFERR_NO_ERROR) {
                    result = true;
                }
                break;
            case AVK_2:
                TRACE("key 2");
                if (MakeMenu() == SFERR_NO_ERROR) {
                    result = true;
                }
                break;
            case AVK_3:
                TRACE("key 3");
                if (MakeIts() == SFERR_NO_ERROR) {
                    result = true;
                }
                break;

            // when the "4" key is pressed
            case AVK_4:
                TRACE("key 4");

                // make TabWindow to place the tab control
                if (MakeTab() == SFERR_NO_ERROR) {

                    result = true;
                }
                break;
        }
    }
    return result;
}

Since the move-item key of the flex list box control in the above code are set to the RIGHT key and the LEFT key and the move-focus key of the tab control are also set to them by default, collision of the key settings occurs.

Therefore, let's set the move-item key of the flex list box control to the UP key and the DOWN key(default settings), if the parent responder of CustomContainer that contains the flex list box control is the tab control.

Example 3.60. Change the move-item key for the flex list box control

// make flex list box control
SFCError CustomContainer::Make(Void)
{
    SFZFlexListBoxControlSmp list;
    SFZFlexListBoxControl::ItemRec item;
    SFBShellSmp shell(SFBShell::GetInstance());
    SFBImageSmp normal(shell->LoadResImage("helloworld.bar", IDI_OBJECT_5001));
    SFBImageSmp select(shell->LoadResImage("helloworld.bar", IDI_OBJECT_5002));
    SFCError error(SFERR_NO_ERROR);

    // create the flex list box control
    if ((list = SFZFlexListBoxControl::NewInstance(&error)) != null) {

        // set the flex list box control's parent responder to CustomContainer
        if ((error = list->SetParent(GetThis())) == SFERR_NO_ERROR) {

            // register the result handler into flex list box control
            error = list->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnListResult)
            );

            if (error == SFERR_NO_ERROR) {

                // ...(omitted)...

                SFYResponderSmp parent;
                if ((parent = GetParent()) != null) {

                    // change the LEFT/RIGHT key settings if CustomContainer's parent is not the tab control
                    // if it is tab control, default settings(UP key and DOWN key) are applied
                    if (parent->GetType() != SFZTabControl::CODE_TYPE) {

                        // set the flex list box control's move-item-up key to the LEFT key
                        list->SetScrollUpKey(AVK_LEFT);

                        // set the flex list box control's move-item-down key to the RIGHT key
                        list->SetScrollDownKey(AVK_RIGHT);
                    }
                }

                // ...(omitted)...

            }
        }
    }
    return error;
}

The key handler of the MyWindow tab page will delete it from the tab control and terminate itself when the CLEAR key is pressed.

For the CustomContainer tab page, the result handler of the flex list box control will delete and terminate it from the tab control when the CLEAR key is pressed.

Example 3.61. Terminate the tab page from the tab control

// MyWindow's key handler
XANDLER_IMPLEMENT_BOOLEVENT(MyWindow, OnKey, invoker, event)
{
    Bool result(false);

    unused(invoker);
    switch (event.GetP16()) {
        case AVK_CLR:

            // delete and terminate MyWindow from the tab control
            Terminate();

            result= true;
            break;
    }
    return result;
}

// result handler of the flex list box control
XANDLER_IMPLEMENT_VOIDRESULT(CustomContainer, OnListResult, invoker, reason, result)
{
    SFYResponderSmp parent;

    unused(invoker);
    unused(result);
    switch (reason) {
        case SFP16_RESULT_ESCAPE:
            if ((parent = GetParent()) != null) {

                if (parent->GetType() == SFZTabControl::CODE_TYPE) {
                    // if CustomContainer's parent is tab control: 

                    // delete and terminate CustomContainer from the tab control
                    Terminate();

                }

                else {
                    // if CustomContainer's parent is ItsWindow: 

                    // send the CLEAR key event to ItsWindow
                    parent->InvokeForward(SFXEvent(SFEVT_KEY, AVK_CLR, 0), false);

                }
            }
            break;
    }
    return;
}

The following is execution result on BREW simulator.

  1. TabWindow will be displayed when the "4" key is pressed.
  2. Select the tab with the "RIGHT" key and the "LEFT" key.
  3. The tab page is closed when the CLEAR key is pressed. After all tab pages are closed, TabWindow will be closed when the CLEAR key is pressed.

Figure 3.34. Initial window (helloworld)

Initial window (helloworld)

Figure 3.35. Page 1 of TabWindow: MyWindow

Page 1 of TabWindow: MyWindow

Figure 3.36. Page 2 of TabWindow: CustomContainer

Page 2 of TabWindow: CustomContainer

Figure 3.37. TabWindow: When there is no tab page

TabWindow: When there is no tab page

Reference: Tab Control and Tab Page[SFZTabControl]

3.7.3. SoftKey Control

Let's make the SoftKey control(SFZSoftKeyControl) in the helloworld application class(i.e., root), which will be bound with the helloworld application class(i.e., root), MyWindow, the text menu, the dialog.

[Note] SoftKey control

SoftKey control(SFZSoftKeyControl) is the control designed to receive the key event[SFEVT_KEY] from SoftKey and send it as the SoftKey event[SFEVT_RESPONDER_SOFTKEY].

The SoftKey event[SFEVT_RESPONDER_SOFTKEY] will be sent only to the "active" responder, which is placed foremost among focused responders bound with the SoftKey control.

When the SoftKey control is used, the key event from SoftKey will not be sent to any responder other than the SoftKey control.

Figure 3.38. Standard style

Standard style

In case of standard style, the SoftKey menu is displayed with its three labels in the bottom line of the device screen. These labels correspond to SoftKey-1, SELECT, and SoftKey-2 from left to right.

Now, let's make the SoftKey control in the helloworld application class(i.e., root), register the SoftKey handler, and bind them.

Example 3.62. Create and place the SoftKey control

// 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;
    ItsWindowSmp _itsWindow;
    SFZTextMenuSmp _textMenu;
    TabWindowSmp _tabWindow;

    // *** added code segments are in bold

    // smart pointer to SFZSoftKeyControl
    SFZSoftKeyControlSmp _softkey;

public:

    static SFCInvokerPtr Factory(Void);

private:

    explicit helloworld(Void) static_throws;
    virtual ~helloworld(Void);
    SFCError MakeMy(Void);
    SFCError MakeMenu(Void);
    SFCError MakeIts(Void);
    SFCError MakeTab(Void);
    SFCError MakeColorDialog(UserColorConstRef color);
    Void SetMenuColors(UserColorConstRef color);

    // register the label texts for the SoftKey menu
    SFCError RegisterText(Void);

    // make the SoftKey menu
    SFCError MakeSoftMenu(UInt32 key);

    // get the device screen region excluding the SoftKey control region
    SFXRectangle GetContentBound(Void) const;

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

    // SoftKey handler of the helloworld application class
    XANDLER_DECLARE_VOIDEVENT(OnSoftKey)

    // SoftKey handler of the text menu
    XANDLER_DECLARE_VOIDEVENT(OnMenuSoftKey)

    // SoftKey handler of MyWindow
    XANDLER_DECLARE_VOIDEVENT(OnMyWindowSoftKey)
};

enum {

    // allocate the identifier to the SoftKey menu
    KEY_MENU_TOP    = 1,
    KEY_MENU_WINDOW,
    KEY_MENU_TEXTMENU,
    KEY_MENU_DIALOG

};

// constructor
helloworld::helloworld(Void) static_throws
{
    if (static_try()) {

        static_throw(RegisterHandler(
            SFXEventRange(SFEVT_RESPONDER_RENDER, SFEVT_RESPONDER_RENDER, SFP16_RENDER_REQUEST, SFP16_RENDER_REQUEST),
            XANDLER_INTERNAL(OnRenderRequest)
        ));

        if (static_try()) {

            static_throw(RegisterHandler(
                SFXEventRange(SFEVT_KEY, SFEVT_KEY, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnKey)
            ));

            if (static_try()) {

                // register the SoftKey handler
                static_throw(RegisterHandler(
                    SFXEventRange(SFEVT_RESPONDER_SOFTKEY, SFEVT_RESPONDER_SOFTKEY, SFP16_BEGIN, SFP16_END),
                    XANDLER_INTERNAL(OnSoftKey)
                ));

                // create the SoftKey control
                if ((_softkey = SFZSoftKeyControl::NewInstance()) != null) {

                    // set the SoftKey control's parent responder to the application class(root)
                    static_throw(_softkey->SetParent(GetThis()));

                    if (static_try()) {

                        // set the SoftKey control's real region to suitable region
                        // (to be set at the bottom in the device screen)
                        // *** CAUTION on specification of GetSuitableBound() only for SFZSoftKeyControl: 
                        // *** 1. A rectangular region will be ignored if specified in the argument and 
                        // ***    the suitable size will be calculated from the full screen region of the mobile phone gotten by calling SFXDevice::GetScreenSize().
                        // ***    In general, the argument is not specified since it has no meaning.
                        // *** 2. This function will return not only the suitable rectangle size but also the suitable origin position.
                        // ***    Therefore, there is no need to set the position of the real region after calling this function.
                        _softkey->SetRealBound(_softkey->GetSuitableBound());

                        // set the SoftKey control's state to "visible" + "active" + "disable" + "unfocus"
                        _softkey->SetState(true, true, false, false);

                        // register the label texts necessary for the SoftKey control
                        static_throw(RegisterSoftText());

                        if (static_try()) {

                            // create the SoftKey menu for the helloworld(initial screen)
                            static_throw(MakeSoftMenu(KEY_MENU_TOP));

                            if (static_try()) {

                                // bind the application class(root) with the SoftKey menu of the SoftKey control
                                static_throw(_softkey->Bind(GetThis(), KEY_MENU_TOP));

                            }
                        }
                    }
                }
                else {

                    static_throw(SFERR_NO_MEMORY);
                }
            }
        }
    }
}
[Caution] CAUTION

In the SFYResponder::GetSuitableBound function of the SoftKey control, the rectangular region will be ignored if specified in the argument. The suitable rectangular region will be calculated from the full screen of the mobile phone gotten by calling the SFXDevice::GetScreenSize function.

The normal SFYResponder::GetSuitableBound function will return only the size of the suitable rectangular region as a result of calculation. However, in case of the SFYResponder::GetSuitableBound function of the SoftKey control, it will return not only the size but also the origin position of the suitable rectangular region. Therefore, there is no need to set the origin position of the real region after calling the SFYResponder::GetSuitableBound function.

All label texts for the SoftKey menu such as "Exit", "Select", "Config", or "Back" are registered into the SoftKey control with the SFZSoftKeyControl::RegisterText function.

Example 3.63. Register label texts for the SoftKey menu

enum {

    // allocate the identifier to each label text for the SoftKey menu
    KEY_TEXT_EXIT   = 1,
    KEY_TEXT_SELECT,
    KEY_TEXT_CONFIG,
    KEY_TEXT_BACK

};

// register the label texts into the SoftKey control
SFCError helloworld::RegisterSoftText(Void)
{
    SFCError error(SFERR_NO_ERROR);

    // register the label texts into the SoftKey control one by one
    // * method to register them together from the resource file is also available
    if ((error = _softkey->RegisterText(KEY_TEXT_EXIT, "Exit")) == SFERR_NO_ERROR) {

        if ((error = _softkey->RegisterText(KEY_TEXT_SELECT, "Select")) == SFERR_NO_ERROR) {

            if ((error = _softkey->RegisterText(KEY_TEXT_CONFIG, "Config")) == SFERR_NO_ERROR) {

                error = _softkey->RegisterText(KEY_TEXT_BACK, "Back");
            }
        }
    }

    return error;
}

The SoftKey menus are made by the MakeSoftMenu function below.

Though only the SoftKey menu for the helloworld(initial screen) is used at present, other SoftKey menus are implemented together in this function as follows:

Example 3.64. Make the SoftKey menu

// make the SoftKey menus
SFCError helloworld::MakeSoftMenu(UInt32 key)
{
    SFCError error(SFERR_NO_ERROR);

    // confirm whether or not the SoftKey menu for menu key has been already made
    if (!_softkey->ContainsMenuKey(key)) {

        // create the SoftKey menu for the menu key
        if ((error = _softkey->CreateMenu(key)) == SFERR_NO_ERROR) {

            // set the label text to the SoftKey menu
            switch (key) {

                // SoftKey menu for the helloworld(initial screen)
                case KEY_MENU_TOP:

                    // set the SoftKey-1's label to "Exit"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SOFTKEY_1, KEY_TEXT_EXIT);

                    // set the SoftKey-2's label to "Config"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SOFTKEY_2, KEY_TEXT_CONFIG);

                    // set SELECT key's label to "false" [nothing will be displayed]
                    _softkey->SetEnable(key, SFZSoftKeyControl::SELECT, false);

                    break;

                // SoftKey menu for the window such as MyWindow
                case KEY_MENU_WINDOW:

                    // set the SoftKey-1's label to "Back"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SOFTKEY_1, KEY_TEXT_BACK);

                    // set the SoftKey-2's label to "Config"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SOFTKEY_2, KEY_TEXT_CONFIG);

                    // set the SELECT key's label to "Select"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SELECT, KEY_TEXT_SELECT);

                    break;

                // SoftKey menu for text menu
                case KEY_MENU_TEXTMENU:

                    // set the SoftKey-1's label to "Exit"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SOFTKEY_1, KEY_TEXT_EXIT);

                    // set the SoftKey-2's label to "false" [nothing will be displayed]
                    _softkey->SetEnable(key, SFZSoftKeyControl::SOFTKEY_2, false);

                    // set the SELECT key's label to "Select"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SELECT, KEY_TEXT_SELECT);

                    break;

                // SoftKey menu for the dialog
                case KEY_MENU_DIALOG:

                    // set the SoftKey-1's label to "false" [nothing will be displayed]
                    _softkey->SetEnable(key, SFZSoftKeyControl::SOFTKEY_1, false);

                    // set the SoftKey-2's label to "false" [nothing will be displayed]
                    _softkey->SetEnable(key, SFZSoftKeyControl::SOFTKEY_2, false);

                    // set the SELECT key's label to "Select"
                    _softkey->SetTextKey(key, SFZSoftKeyControl::SELECT, KEY_TEXT_SELECT);

                    break;

                default:
                    error = SFERR_FAILED;

                    // cancel to make the SoftKey menu
                    _softkey->DestroyMenu(key);

                    break;
            }
        }
    }
    return error;
}

Since the region to be displayed is reduced by the SoftKey control's region, the real region of window(MyWindow) or the text menu(_textMenu) needs to be adjusted.

The following is the code to implement the GetContentBound function which gets the region of the device screen excluding that of the SoftKey control.

Example 3.65. Get the region of the device screen excluding that of the SoftKey control

// get the device screen region excluding the SoftKey control's region
SFXRectangle helloworld::GetContentBound(Void) const
{
    SFXRectangle result;

    result.Set(GetLocalBound());
    result.SetBottom(_softkey->GetRealBound().GetTop());
    return result;
}

For each of the other respondes(MyWindow, ItsWindow, TabWindow, text menu, dialog), processing is the same with the helloworld application class as below.

Each real region of responders other than dialog will be changed by placing the SoftKey control. Therefore, we have to set it to the suitable size by using the helloworld::GetContentBound function as the code below.

Example 3.66. Make the other responders

// MyWindow make
SFCError helloworld::MakeMy(Void)
{
    SFCError error(SFERR_NO_ERROR);

    if ((_myWindow = MyWindow::NewInstance(&error)) != null) {

        error = _myWindow->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            // register the SoftKey handler
            error = _myWindow->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_SOFTKEY, SFEVT_RESPONDER_SOFTKEY, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnWindowSoftKey)
            );
            if (error == SFERR_NO_ERROR) {

                // set the MyWindow's real region to the region obtained by deflating the content region(device screen region excluding the SoftKey control) by (10, 10)
                _myWindow->SetRealBound(GetContentBound().Deflate(10, 10));

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

                // create SoftKey menu
                if ((error = MakeSoftMenu(KEY_MENU_WINDOW)) == SFERR_NO_ERROR) {

                    // bind MyWindow with the SoftKey menu of the SoftKey control
                    error = _softkey->Bind(_myWindow, KEY_MENU_WINDOW);

                    if (error == SFERR_NO_ERROR) {

                        _myWindow->ToFront();
                        _myWindow->Make();
                    }
                }
            }
        }
    }
    return error;
}

// ItsWindow make
SFCError helloworld::MakeIts(Void)
{
    // ...(omitted)...
}

// TabWindow make
SFCError helloworld::MakeTab(Void)
{
    // ...(omitted)...
}

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

    if ((_textMenu = SFZTextMenu::NewInstance(&error)) != null) {

        error = _textMenu->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            error = _textMenu->RegisterHandler(
                SFXEventRange(SFEVT_RESPONDER_RESULT, SFEVT_RESPONDER_RESULT, SFP16_BEGIN, SFP16_END),
                XANDLER_INTERNAL(OnMenuResult)
            );
            if (error == SFERR_NO_ERROR) {

                // register the SoftKey handler into the text menu
                error = _textMenu->RegisterHandler(
                    SFXEventRange(SFEVT_RESPONDER_SOFTKEY, SFEVT_RESPONDER_SOFTKEY, SFP16_BEGIN, SFP16_END),
                    XANDLER_INTERNAL(OnMenuSoftKey)
                );
                if (error == SFERR_NO_ERROR) {

                    // ...(omitted)...

                    // set the text menu's real region to the content region
                    _textMenu->SetRealBound(GetContentBound());

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

                    // make SoftKey men
                    if ((error = MakeSoftMenu(KEY_MENU_TEXTMENU)) == SFERR_NO_ERROR) {

                        // bind the text menu with the SoftKey menu of the SoftKey control
                        error = _softkey->Bind(_textMenu, KEY_MENU_TEXTMENU);

                        if (error == SFERR_NO_ERROR) {

                            _textMenu->ToFront();
                        }
                    }
                }
            }
        }
    }
    return error;
}

// make the dialog
SFCError helloworld::MakeColorDialog(UserColorConstRef color)
{
    SFZMessageDialogSmp dlg;
    SFCError error(SFERR_NO_ERROR);

    if ((dlg = SFZMessageDialog::NewInstance(&error)) != null) {

        error = dlg->SetParent(GetThis());
        if (error == SFERR_NO_ERROR) {

            // don't register handler since this dialog does not handle the SoftKey
            // ...(omitted)...

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

            // make the SoftKey menu for the dialog
            if ((error = MakeSoftMenu(KEY_MENU_DIALOG)) == SFERR_NO_ERROR) {

                // bind dialog with the SoftKey menu of the SoftKey control
                error = _softkey->Bind(dlg, KEY_MENU_DIALOG);

                if (error == SFERR_NO_ERROR) {

                    dlg->ToFront();
                    dlg->ScheduleTimer(500);
                }
            }
        }
    }
    return error;
}

Next, let's implement the SoftKey handler of each responder according to the following specifications.

  1. If the application class(i.e., root) is the active responder, terminate itself by "Exit" and open the text menu by "Config".
  2. If the window is the active responder, terminate itself by "Back" and open the text menu by "Config".
  3. If the text menu is the active responder, terminate itself by "Back".
  4. If the dialog is the active responder, there is no operation associated with the SoftKey.
[Note] Active responder

The responder placed foremost among focused responders bound with the SoftKey control is called as the active responder.

The SoftKey event will be sent only to the active responder.

Example 3.67. Implement the SoftKey handler

// SoftKey handler of the helloworld application class
XANDLER_IMPLEMENT_VOIDEVENT(helloworld, OnSoftKey, invoker, event)
{
    unused(invoker);
    switch (event.GetP16()) {
        case AVK_SOFT1:

            // terminate the helloworld applet
            Terminate();

            break;
        case AVK_SOFT2:

            // make the text menu
            MakeMenu();

            break;
    }
    return;
}

// SoftKey handler of the window
XANDLER_IMPLEMENT_VOIDEVENT(helloworld, OnWindowSoftKey, invoker, event)
{
    switch (event.GetP16()) {
        case AVK_SOFT1:

            // terminate the window
            invoker->Terminate();

            break;
        case AVK_SOFT2:

            // make the text menu
            MakeMenu();

            break;
    }
    return;
}

// SoftKey handler of the text menu
XANDLER_IMPLEMENT_VOIDEVENT(helloworld, OnMenuSoftKey, invoker, event)
{
    switch (event.GetP16()) {
        case AVK_SOFT1:

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

            break;
    }
    return;
}

Now, since there is no need to terminate the helloworld applet by pushing the SELECT key in the initial screen, the key handler of the helloworld application class is updated as follows:

Example 3.68. Update the key handler of the helloworld application class

// key handler 
XANDLER_IMPLEMENT_BOOLEVENT(helloworld, OnKey, invoker, event)
{
    SFYResponderSmp child;
    Bool result(false);
    unused(invoker);

    // handle the key event only if application class has no visible child responder
    // * search from 2nd foremost responder since SoftKey control is always placed foremost
    if ((child = GetChildForward(1, true, false, false, false)) == null) {

        switch (event.GetP16()) {

            // due to the code below, the SELECT key will not be handled
            #if 0
            case AVK_SELECT:
                Terminate();
                result = true;
                break;
            #endif

            // ...(omitted)...

        }
    }
    return result;
}

The following is execution result on BREW simulator.

Figure 3.39. Initial screen

Initial screen

Figure 3.40. MyWindow screen

MyWindow screen

Figure 3.41. TextMenu screen

TextMenu screen

Figure 3.42. Dialog screen

Dialog screen

Figure 3.43. ItsWindow screen

ItsWindow screen

Figure 3.44. TabWindow screen

TabWindow screen