PrevNextUpHome SophiaFramework UNIVERSE 5.3

3.2. Window

3.2.1. Defining and Implementing the Window

Window is the responder designed to place various controls or containers.

An application class can have multiple windows piled up and displayed at the same time.

In this section, we will customize the code of the helloworld applet generated by the SophiaFramework AppWizard to display the string "Hello Window" in the window as the figure below.

3.2.1.1. Defining MyWindow

First, let's define and implement the window(MyWindow) inheriting from SFZWindow as follows:

Figure 3.2. Drawing the string "Hello Window" in the center of MyWindow

Drawing the string "Hello Window" in the center of MyWindow

MyWindow is specified as follows:

  1. MyWindow will be created and the string "Hello Window" will be drawn in the center of MyWindow after the "1" key is pressed in the initial screen.
  2. MyWindow will be closed when the CLEAR key is pressed.

The following is the code to define the custom window(MyWindow) which inherits from SFZWindow.

Example 3.9. Define MyWindow

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

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

public:

    // since small alphabet is reserved for 4-character literals, 
    // define ('M', 'W', 'N', 'D') as 4-character literals for MyWindow with capital alphabet
    enum CodeEnum {
        CODE_TYPE = four_char_code('M', 'W', 'N', 'D')
    };

public:

    // generate the MyWindow instance managed by the smart pointer
    static MyWindowSmp NewInstance(SFCErrorPtr exception = null);

protected:

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

    // drawing handler(override the HandleRenderRequest virtual function booted up first when the drawing event is received)
    virtual Void HandleRenderRequest(SFXGraphicsPtr graphics) const; 

private:

    // key handler
    XANDLER_DECLARE_BOOLEVENT(OnKey)
};
[Tip] SFMTYPEDEFRESPONDER macro

SFMTYPEDEFRESPONDER is the macro to generate the useful user-defined types regarding the responder class specified as an argument. For instance, the smart pointer type of the responder given as an argument is defined by this macro.

[Tip] SFMRESPONDERINSTANTIATE macro

SFMRESPONDERINSTANTIATE is the macro to avoid a bug of RealView Compilation Tools for BREW 1.2 compiler. This macro is ignored in compilers other than RealView Compilation Tools for BREW 1.2.

When the responder class is defined newly, you have to describe the inherihance relation from SFYResponder to newly defined class with SFMRESPONDERINSTANTIATEZERO, SFMRESPONDERINSTANTIATEONE, SFMRESPONDERINSTANTIATETWO, SFMRESPONDERINSTANTIATETHREE, SFMRESPONDERINSTANTIATEFOUR, SFMRESPONDERINSTANTIATEFIVE, SFMRESPONDERINSTANTIATESIX, SFMRESPONDERINSTANTIATESEVEN depending on the number of inheritance layers.

For instance, think of MyWindow as above. MyWindow, SFZWindow, SFYContainer, and SFYWidget inherit from SFZWindow, SFYContainer, SFYWidget, and SFYWidget respectively. Since there is four layers from SFYResponder to MyWindow, use the SFMRESPONDERINSTANTIATEFOUR macro.

If the number of inheritance layers from SFYResponder is three, use the SFMRESPONDERINSTANTIATETHREE macro. If five layers, use the SFMRESPONDERINSTANTIATEFIVE macro. This macro is provided with up to the SFMRESPONDERINSTANTIATESEVEN macro which is used in inheriting from seven layers.

[Important] Creating the responder: NewInstance function

The responder instance accessed through the smart pointer(SFXResponderPointer) must be created not with the "new" operator but with the NewInstance function.

The responder class which has responder instances needs to have the NewInstance function defined and implemented with the SFYResponder::Factory function.

[Note] Drawing the responder

To draw the responder from the outside, register the drawing handler to draw itself, like the code of the helloworld application class in the previous section.

To draw the responder by itself, override the HandleRenderRequest function booted up when the drawing event is received, and describe the drawing procedure.

Cautions on the drawing handler are as follows:

  1. When the HandleRenderRequest funtion is overridden, the content of its parent's HandleRenderRequest() funtion will be overridden completely.
  2. First, the HandleRenderRequest function will be called, and then the drawing handlers will be booted up in the registered order.
  3. In case the transparency attribute is not set with the SFYResponder::SetPropertyTransparent function, the responder region will be filled in the background color(by default, white color) set with the SFYWidget::SetBackgroundColor function before the drawing handler boots up.

[Caution] Responder type

When the new responder class inheriting from SFYResponder is defined, you are recommended to set the resopnder type of the 4-character literal with the four_char_code macro.

Since the 4-character literals consists of small alphabet are reserved by SophiaFramework UNIVERSE, the 4-character literal consisting of capital alphabet is recommended as the responder type of the user-defined responder class.

3.2.1.2. Defining the helloworld application class

Let's declare the smart pointer of MyWindow defined above and the function to make the MyWindow instance as the member of the helloworld application class.

Example 3.10. 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:

    // *** added code segments are in bold

    MyWindowSmp _myWindow; // smart pointer of MyWindow

public:

    static SFCInvokerPtr Factory(Void);

private:

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

    // make MyWindow
    SFCError Make(Void); 

    XANDLER_DECLARE_VOIDRENDER(OnRenderRequest) // declare the drawing handler
    XANDLER_DECLARE_BOOLEVENT(OnKey)            // declare the key handler
};
[Note] Smart pointer

Since the responder instance is accessed through the smart pointer, it is necessary to create it with the NewInstance function and process it as an instance of the "smart pointer" class whose name has "Smp" as a suffix.

The type of the "smart pointer" class is defined in the SFMTYPEDEFRESPONDER macro before the class declaration. For instance, MyWindowSmp is defined in the SFMTYPEDEFRESPONDER(MyWindow) macro.

3.2.1.3. Implementing MyWindow

Let's implement the NewInstance function to create the MyWindow instance accessed through the smart pointer, the constructor, the destructor, the drawing handler, and the key handler of MyWindow.

Example 3.11. Implement MyWindow

// create the MyWindow instance
MyWindowSmp MyWindow::NewInstance(SFCErrorPtr exception)
{

   // create the MyWindow instance with Factory()
   // since Factory() returns the SFYResponderSmp type
   // downcast into the MyWindowSmp type with the static_pointer_cast operator
   return static_pointer_cast<MyWindow>(Factory(:: new MyWindow, exception));

}

// define the color scheme
// light green color for MyWindow's background
#define COLOR_MY_WINDOW_BACK        (SFXRGBColor(0xCC, 0xFF, 0xCC, 0x00))

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

        // four_char_code('M', 'W', 'N', 'D') is set as the responder type
        SetType(CODE_TYPE);

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

        // since MyWindow::HandleRenderRequest function will be called first when the drawing event is received
        // it is enough to describe the drawing processing of MyWindow in this function
        // it is unnecessary to define, implement, register the drawing handler newly into MyWindow

        // set the MyWindow's background color to light green
        SetBackgroundColor(COLOR_MY_WINDOW_BACK);
    }
}

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

// define the color scheme
// black color for string
#define COLOR_BLACK                 (SFXRGBColor(0x00, 0x00, 0x00, 0x00))

// drawing handler
Void MyWindow::HandleRenderRequest(SFXGraphicsPtr graphics) const
{
    // SFXGraphics instance for drawing is passed as the 1st argument(graphics)
    // left-top of MyWindow's local region is origin of the drawing region

    // MyWindow's local region is filled in light green, 
    // which is set in the constructor with SFYWidget::SetBackgroundColor()

    // draw "Hello Window" string in black color at the center of MyWindow's local region
    graphics->DrawSingleText("Hello Window", GetLocalBound(), COLOR_BLACK);

    // there is no need to execute "graphics->Update();" when the GUI framework is used

    return;
}

// key handler (called when MyWindow is focused and some key is pressed) 
// invoker is the responder of the SFYResponderPtr type which called this handler(in this case, MyWindow)
// event is key event of SFXEventConstRef type
XANDLER_IMPLEMENT_BOOLEVENT(MyWindow, OnKey, invoker, event)
{
    switch (event.GetP16()) {
        case AVK_CLR: // when the CLEAR key is pressed

            // close MyWindow
            invoker->Terminate();

            // return "true" since the key event is handled
            return true;

        case AVK_1: // when the "1" key is pressed

            // draw "1-key" in BREW Output Window
            TRACE("1-key");

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

    // return "false" since the key event is NOT handled
    return false;
}
[Tip] static_pointer_cast operator

Since the SFYResponder::Factory function returns the SFYResponderSmp type, you have to downcast it into the MyWindowSmp type with the static_pointer_cast operator.

[Tip] SFYWidget::SetBackgroundColor function

In the responder inheriting from SFYWidget, the background of the local region will be filled in the color[default: white] set with the SFYWidget::SetBackgroundColor function before the SFYWidget::HandleRenderRequest function is executed.

In the constructor of MyWindow, the background color is set to light green with this function.

[Tip] HandleRenderRequest function

When the responder receives the drawing event, it will boot up the HandleRenderRequest function first.

If the HandleRenderRequest function is overridden to implement the drawing processing, there is no need to register another drawing handler into the responder. At this time, you must take care that the content of the HandleRenderRequest function implemented in the parent class will be overridden completely.

[Note] When the drawing event is received

In case of the helloworld applet, the drawing event will be sent by the renderer booted up by the SFYResponder::Render function, which will be called in the end of the event loop of the SFEVT_APP_START event. at the applet start.

[Note] Drawing the region of MyWindow

MyWindow is drawn in the local region with the graphics object.

[Note] Key event which is not handled in MyWindow

The key event which is not handled in MyWindow will be delegated to the root[SFZRoot] which the helloworld application class, parent resopnder of MyWindow, contains by default.

If it is not handled by the root in the end, the renderer will not be booted up.

3.2.2. Making the Window

3.2.2.1. Making the Responders in the Window

The procedure to make the responder is as follows:

  1. [Required] Create the responder instance (called "window" in the below) with the NewInstance function.
  2. [Required] Set the parent responder of "window" with the SFYResponder::SetParent function.
  3. [Required] Set the real region of "window" in the parent responder's local region coordinate system with the SFYResponder::SetRealBound function.
  4. [Required] Set the state of "window" with the SFYResponder::SetState function.
  5. [Optional] Set the other parameters such as the background color which are defined for "window", if necessary.
  6. [Optional] Move "window" and its child responders foremost with the SFYResponder::ToFront function.

Example 3.12. Make MyWindow

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

    // create the MyWindow instance(called "_myWindow" in the below)
    if ((_myWindow = MyWindow::NewInstance(&error)) != null) {

        // obtain the root which the application class contains internally with SFYApplication::GetThis()
        // set the parent responder of _myWindow to the root
        error = _myWindow->SetParent(GetThis());

        if (error == SFERR_NO_ERROR) {

            // set the real region of _myWindow to the region obtained by deflating the device screen region by (10, 10)
            _myWindow->SetRealBound(GetLocalBound().Deflate(10, 10));

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

            // move _myWindow foremost
            _myWindow->ToFront();
        }
    }
    return error;
}
[Note] SFYApplication::GetThis function

The SFYApplication::GetThis function returns the root[SFZRoot] which the application class contains by default.

[Note] SFYApplication.GetLocalBound function

The SFYApplication::GetLocalBound function returns the local region (i.e., the rectangular region of the device screen) of the root[SFZRoot] which the application class contains by default.

[Tip] SFXRectangle::Deflate function

If the rectangle represented with SFXRectangle(x, y, w, h) is deflated by SFXSize(a, b), then it will be the rectangle of which the left-top is SFXGrid(x + a, y + b) and the size is SFXSize(w - 2 * a, h - 2 * b).

For instance, if the local region(device screen) of the application class(i.e., root[SFZRoot]) returned by SFYApplication::GetLocalBound() is SFXRectangle(0, 0, 240, 320), the region after deflated by SFXSize(10, 10) will be SFXRectangle(10, 10, 220, 300).

Reference: SFXRectangle::Deflate

[Note] Default virtual region and local region

The virtual region of the responder will be physically the same with the real region if it is not defined. Since the virtual region is physically the same with the local region, the local region eqauls the real region at this time.

The region of _myWindow is (0, 0, 220, 300) in the its local region coordinate system.

* Drawing processing of the responder will be performed in the local region.

[Note] Real region

Real region is the visible rectangular region of the responder. It is defined in the relative coordinate system of the local region of the parent responder.

If the virtual region of the responder is not set, the local region equals the real region, and therefore all the local region is visible. If the virtual region is set, only the real region is visible.

[Note] Order relation amon the responders

The responders are managed by the responder tree, and drawn on the device screen according to the order relation of the tree structure.

In the order relation of the responder tree, the child responder is placed more foreground than the parent responder, and the older brother responder is placed more foreground than the younger brother responder.

The child responder of the older brother responder is placed more foreground than the younger brother responder.

By moving the responder foremost among the sibling responders, the child responders will be automatically moved more foreground than other sibling responders.

To move the responder foremost among the sibling responders, call the SFYResponder::ToFront function.

* By calling the SFYResponder::ToFront function of the container responder such as a window or a dialog inheriting from SFYContainer, the controls such as a button, a label, or a tab in the container responder will be moved together foremost.

[Tip] SFYResponder::ToFront / SFYResponder::ToBack function

The SFYResponder::ToFront or SFYResponder::ToBack function is used to move the responder such as a window or a dialog together with the child responders foremost or backmost among the sibling responders respectively.

[Caution] Caution

When the responder is appended on the responder tree, it will be placed backmost among the sibling responders.

To display it on the device screen, it is necessary to move it foremost with the SFYResponder::ToFront function.

3.2.2.2. Updating the Key Handler of the helloworld application class

Let's update the key handler of the helloworld application class to display the window(MyWindow) when the "1" key is pressed in the initial screen.

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

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

    // key handler
    switch (event.GetP16()) {
        case AVK_SELECT: // when the SELECT key is pressed
            Terminate(); // terminate the helloworld applet
            return true;

        // *** added code segments are in bold

        case AVK_1: // when the "1" key is pressed

            // make MyWindow
            if (Make() != SFERR_NO_ERROR) {
                return false;
            }

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

    // return "false" since the key event is NOT handled
    return false;
}

3.2.3. Terminating the Window

To close the responder such as a window or a dialog displayed on the device screen, call the SFYResponder::Terminate function.

Example 3.14. Key handler of MyWindow: terminate MyWindow when the CLEAR key is pressed

// key handler: terminate MyWindow when the CLEAR key is pressed
XANDLER_IMPLEMENT_BOOLEVENT(MyWindow, OnKey, invoker, event)
{
    switch (event.GetP16()) {
        case AVK_CLR: // when the CLEAR key is pressed

            // terminate MyWindow
            // invoker: pointer to MyWindow (SFYResponderPtr type)
            invoker->Terminate();

            // in this phase,  MyWindow still remains on the heap memory
            // MyWindow will be released from the heap memory when the reference count becomes 0

            // since MyWindow is referred to by _myWindow of the helloworld application class
            // MyWindow will be released from the heap memory when the helloworld applet terminates

            return true;

        case AVK_1:

    // ... (omitted) ...

}
[Important] SFYResponder::Terminate function

When the responder is terminated with the SFYResponder::Terminate function, it will be cut off from the responder tree and its valid state flag will be set to "false".

The terminated responder will be automatically released from the heap memory when its reference count becomes 0.

This function will be automatically called when the reference count becomes 0. For almost all responders, there is no need call this function .

In the temporary responder such as a menu or a dialog which needs to be closed immediately after the user operation, calling this function explicitly is necessary to terminate it on the spot.

But it will remain on the heap memory if it is refered to by the smart pointer. To release it from the heap memory immediately, make its reference count 0 by calling the smart pointer's SFXResponderPointer::Release function or substituting another responder into the smart pointer.

Reference: SFYResponder::Terminate | SFXResponderPointer::Release | Responder Tree

[Tip] Tip

The SFCApplication::Terminate function is only for finalizing the applet.

[Caution] "delete" operator

You MUST NOT execute the delete operator on the responder in the responder tree.

If the responder is deleted from the heap memory with the delete operator, inconsistency may occur since it is assumed to exist in the responder tree.

To close the responder, use the SFYResponder::Terminate function.

3.2.4. Executing on BREW Simulator

  1. MyWindow will be displayed when the "1" key is presssed in the initial screen.
  2. "1-key" will be displayed in BREW Output Window when the "1" key is pressed in MyWindow.
  3. MyWindow will close when the CLEAR key is pressed in MyWindow.

Figure 3.3. Screen after the "1" key is presssed in the initial screen

Screen after the "1" key is presssed in the initial screen

Figure 3.4. BREW Output Window after the "1" key is pressed in MyWindow

BREW Output Window after the "1" key is pressed in MyWindow
[Note] Note

BREW Output Window can be displayed by clicking the following on BREW Simulator.

[menu]->[display]->[Output Window]

Reference: Window(Basic)