Home > Products > SophiaFramework UNIVERSE > GUI Framework

SophiaFramework UNIVERSE Architecture

Index

  1. What's the GUI Framework?
  2. How to Use the GUI Framework
  3. Structure of the GUI Framework
  4. Event Handling
    1. Event Driven Structure
    2. Booting up Application
    3. Terminating Application
    4. Tracer
    5. Standard Tracer
    6. Handler
  5. Rendering
    1. Timing to be Rendered
    2. Handler
    3. Rendered Region
    4. Explicit Rendering
  6. Appendix: GUI Framework of SophiaFramework 2.2

What's the GUI Framework?

The GUI Framework is an application framework which automates GUI-related processes such as event handling and rendering, and offers standard GUI components including window, dialog, menu, button, label, checkbox, etc.

With the GUI Framework, there is no need to program complex codes on event handling or rendering the GUI components.

SophiaFramework UNIVERSE provides the GUI components called Responder as the GUI Framework.

Responder List

Application
Class Name Description
SFRApplication Application base class *

Window
Class Name Description
SFRWindow Window base class *
SFRPlainWindow Plain window without frame
SFRFrameWindow Plain window with frame
SFRTitleWindow 3-D window with frame and title bar

Dialog
Class Name Description
SFRDialog Dialog base class *
SFRPlainDialog Plain dialog without frame
SFRFrameDialog Plain dialog with frame
SFRTitleDialog 3-D dialog with frame and title bar
SFRMessageDialog Message dialog with at most 1 button
SFRMultiDialog Selection dialog with at most 3 buttons

Menu
Class Name Description
SFRMenu Menu base class *
SFRTextMenu Menu displaying text-based items

Pane
Class Name Description
SFRPane Plain base class *
SFRPlainPane Plain pane without frame
SFRTabPane Plain pane reserved for SFRTabControl

Control
Class Name Description
SFRControl Control base class *
SFRLabelControl Label control
SFRButtonControl Button control
SFRCheckboxControl Checkbox control
SFRRadiobuttonControl Radio button control
SFRComboboxControl Combo box control
SFRTabControl Tab control
SFREditboxControl Text input control
SFRBrowserControl Control displaying HTML browser

* The Responder base classes are used to create custom GUI components.

How to Use the GUI Framework

To use the GUI Framework you need to create an Application class which inherits from the SFRApplication class. When creating a project using Application Wizard, choose "Yes" when asked "Is the GUI framework used?" The automatically generated application class can be used as a template.

The Application class must have the same name with the application itself, such as "HelloWorld" or "MyApp".

Example : Creating a Window
SFRWindowPtr window;

window = ::new SFRTitleWindow(SFRApplication::GetInstance(),
                              SFXRectangle(0, 0, 240, 320), 
                              "my window"); 
Example : Creating a Button
SFRButtonControlPtr button;

button = ::new SFRButtonControl(window, 
                                SFXRectangle(10, 10, 128, 24), 
                                "my button"); 
Example : Displaying Debugging Output
button->RegisterHandler(SREVT_CONTROL, HANDLER_BEFORE, HANDLER_FUNCTION(OnButton));

HANDLER_IMPLEMENT_VOIDCONTROL(MyApp, OnButton, result, control)
{
    TRACE("my button was pushed.");
    return;
} 

An application with complex GUI can be easily developed by configuring the Control objects such as Button and Checkbox in the Window object and registering functions that are called when the Contol objects is operated.

Structure of the GUI Framework

In the GUI Framework, the Application class inherits from the SFRApplication class. The Application object controls the Window objects, and the Window object controls the Control objects. The diagram below shows its hierarchy.

Diagram : Inheritance Relation

SophiaFramework UNIVERSE GUI Inheritance

Diagram : Ownership Relation

SophiaFramework GUI Ownership

The Dialog, Menu and Window classes are on the same hierarchical level. The Pane object controls the Control objects; The Pane object can be controlled by the Control object as well.

This hierarchy automates event handling and rendering the GUI components in the BREW environment.

Event Handling

The event triggered by user operation is sent to the application by the BREW environment. After the application receives the event, Event Handling Engine handles it.

According to the Tracer rules, Event Handling Engine distributes each BREW event to the appropriate Responder objects, and executes the functions called Handler registered by the developer.

Event Driven Structure

The BREW application has the event driven structure, which means that an event triggered by user operation or external interruption is dispatched and handled.

The application processes such as initialization or key operation are done depending on the event type.

From the application boot-up to its termination, a variety of events from the BREW environment are dispatched to the application.

Diagram : Lifecycle

シーケンス図 : ライフサイクル

With the GUI Framework, there is no need to write complex code for event handling by hand from scratch. Because Event Handling Engine automatically dispatches each event to the appropriate handler function.

Booting up Application

When a user boots up an application, Event Handling Engine instantiates once the Application class which inherits from the SFRApplication class.

Next, the SFEVT_APP_START event is dispatched to the application from the BREW environment, and the application initialization begins.

Sequence Diagram : Booting up Application

Sequence Diagram : Booting up Application

Initialization process such as creating a Window obeject is processed within the constructor of Application class or the handler for the SFEVT_APP_START event.

Terminating Application

When a user terminates the application by selecting the QUIT option in the menu or by pressing the POWER key, the SFEVT_APP_NO_CLOSE event is dispatched from the BREW environment. Then, Event Handling Engine returns "false" to this event.

* about SFEVT_APP_NO_CLOSE : Return "false" to this event when the application should be ended. But to make it not be ended, return "true" to this event.

Next, the SFEVT_APP_STOP event is dispatched from the BREW environment, and its termination begins.

When the handling for the SFEVT_APP_STOP event finishes normally, Event Handling Engine is released, and the application itself is also automatically released.

Sequence Diagram : Terminating Application

Sequence Diagram : Terminating an Application

The diagram above shows that the user pressed the QUIT button in the Window object; the SFRApplication::Terminate() function is executed and the application is ended.

Termination process such as destroying the Window object are done within the Application destructor or the handler for the SFEVT_APP_STOP event.

Tracer

Each event is dispatched to the appropriate Responder objects according to its type by Event Handling Engine.

For instance, the KEY event is dispatched to the Responder objects that are focused for user operation, and the SUSPEND or RESUME event is dispatched to all Responder objects.

This complex event dispatching is controlled by the rules set forth by what is called Tracer.

SFEVT_KEY Event Flow Chart

SFEVT_KEY Event Flow Chart

SFEVT_APP_RESUME Event Flow Chart

SFEVT_APP_RESUME Event Flow Chart

The GUI framework includes Standard Tracer that can be overridden if necessary. The Child Responder class inherits the Tracer rules from its Parent Responder class. Unless the settings are overridden explicitly, Standard Tracer registered in the Application class is used.

The RegisterTracer() function is used to register the Tracer rules, which are shown below. The settings for event dispatching can be done according to the Responder status.

Tracer Rule Description
TRACER_NONE Does not dispatch to Child Responder objects
TRACER_FORWARD Dispatches from foreground to background Child Responder objects
TRACER_BACKWARD Dispatches from background to foreground Child Responder objects
TRACER_FOCUS Dispatches to focused Child Responder objects
TRACER_PROVIDE Permits duplicated event handling

Example : Registering a Tracer
class MyApp : public SFRApplication {
    public:
        explicit MyApp(Void) static_throws;
};

MyApp::MyApp(Void) static_throws
{
    if (static_try()) {

        // dispatch the SOFTKEY 1-3 events to all Responder objects
        // from foreground to background 
        //
        // as Child Responder inherites Tracer rules,
        // these are valid for Window and Control

        static_throw(RegisterTracer(SFEVT_KEY,
                                    AVK_SOFT1, 
                                    AVK_SOFT3, 
                                    BEHAVIOR_NONE, 
                                    TRACER_FORWARD));
    }
    return;
}

Standard Tracer

The GUI Framework includes the Tracer rules indicated below as Standard Tracer.

Dispatching Order : In case of "-", the event is not dispatched to the Child Responder objects.

Duplicate Event Handling : In case of "-", the event is not duplicately handled. If an event is handled by a Responder object, it will be never dispatched to the other Responder objects.

Status Filter : In case of "Ο", the event can be dispatched to the Responder object according to its status. In case of blank space, the event can be dispatched to the Responder object independently of its status.

Table : Standard Tracer
Event Range Dispatching Order Duplicate Event Handling Status Filter *
      V E F T
Tracer rules on BREW Events included in the SFRApplication Class
SFEVT_APPLICATION_CLASS_BEGIN
~ SFEVT_APPLICATION_CLASS_END
- -        
SFEVT_APP_RESUME Foreground to Background Permitted        
SFEVT_APP_SUSPEND

Foreground to Background Permitted

       
SFEVT_KEY_CLASS_BEGIN
~ SFEVT_KEY_CLASS_END
Focused - Ο Ο Ο Ο
SFEVT_CONTROL_CLASS_BEGIN
~ SFEVT_CONTROL_CLASS_END
Focused - Ο Ο Ο Ο
SFEVT_CTL_TAB Focused Permitted Ο Ο Ο Ο
SFEVT_CTL_TEXT_MODECHANGED Focused Permitted Ο Ο Ο Ο
SFEVT_DIALOG_CLASS_BEGIN
~ SFEVT_DIALOG_CLASS_END
Focused - Ο Ο Ο Ο
SFEVT_SHELL_CLASS_BEGIN
~ SFEVT_SHELL_CLASS_END
- Permitted        
SFEVT_DEVICE_CLASS_BEGIN
~ SFEVT_DEVICE_CLASS_END
- Permitted        
SFEVT_CLIPBOARD_CLASS_BEGIN
~ SFEVT_CLIPBOARD_CLASS_END
- Permitted        
Tracer rules on SophiaFramework UNIVERSE Events included in the SFRApplication Class
SREVT_RESPONDER_INITIALIZE - Permitted        
SREVT_RESPONDER_TERMINATE - -        
SREVT_RESPONDER_TERMINATE,
SRP16_TERMINATE_TRY
Foreground to Background Permitted        
SREVT_RESPONDER_RENDER - Permitted Ο      
SREVT_RESPONDER_STATUS - -        
SREVT_APPLICATION - -        
Tracer rules on SophiaFramework UNIVERSE Event included in the SFRWindow Class
SREVT_WINDOW - -        
Tracer rules on SophiaFramework UNIVERSE Event included in the SFRDialog Class
SREVT_DIALOG - -        
Tracer rules on SophiaFramework UNIVERSE Event included in the SFRMenu Class
SREVT_MENU - -        
Tracer rules on SophiaFramework UNIVERSE Event included in the SFRPane Class
SREVT_PANE - -        
Tracer rules on SophiaFramework UNIVERSE Event included in the SFRControl Class
SREVT_CONTROL - -        

* Status Filter Items in the above Tracer rules
Comparison Item Description
V Visible or not
E Can be operated or not
F Focused or not
T Targeted or not

Handler

A function for handling an event triggered by user operation or network communication is called Handler.

Each event has its priority to be handled according to its type. For instance, the KEY event is handled at the lowest Responder object in its hierarchy, because a user operates the foreground Responder object that has focus.

Diagram : Priority of Event Handling

Diagram : Event Hnadling Priority

To implement the above scheme, the Parent Responder object must handle the events after the Child Responder objects. Thus, there are two types of Handlers: Before-Handler and After-Handler.

Before-Hander is a handler called before an event is dispatched to the Child Responder objects, and After-Handler is a handler called after an event is handled by the Child Responder objects.

The RegisterHanlder() function is used to register a handler. The possible rules for the handler settings are shown below.

Handler Type Description
HANDLER_BEFORE Handles an event before the Child Responder objects
HANDLER_AFTER Handles an event after the Child Responder objects

The following Macro functions are used to create handlers ( Specialized Macro functions are also available )

Macro Description
HANDLER_DECLARE_BOOLEVENT Macro for declaration
HANDLER_IMPLEMENT_BOOLEVENT Macro for implementation

Example : Registering and Implementing a Key Handler
class MyWin : public SFRPlainWindow {
    public:
        explicit MyWin(Void) static_throws;
    private:
        HANDLER_DECLARE_BOOLEVENT(OnKey)
};

MyWin::MyWin(Void) static_throws : SFRPlainWindow(...)
{
    if (static_try()) {

        // Register a handler OnKey for the Key event

        static_throw(RegisterHandler(SFEVT_KEY, 
                                     HANDLER_AFTER, 
                                     HANDLER_FUNCTION(OnKey)));
    }
    return;
}
HANDLER_IMPLEMENT_BOOLEVENT(MyWin, OnKey, event)
{
    switch (event.GetP16()) {
        case AVK_SELECT:
            DoSelect();
            return true;
        case AVK_CLR:
            DoClear();
            return true;
    }
    return false;
}

The Handler function returns true if the event is handled, or else false. In case of true, Event Handling Engine creates the Rendering event that boots up Rendering Engine if necessary.

Rendering

When Event Handling Engine finishes, Rendering Engine boots up if necessary. Rendering Engine optimizes its execution based on the status and configuration of the Responder objects.

Within each event loop, only the necessary rendering handlers are called such that performance will not deteriorate even with complex screen rendering.

Timing to be Rendered

The screen is rendered in the final stage of each event loop. When the event handler returns true, Event Handling Engine boots up Rendering Engine, which checks whether the rendering regions are registered or not.

Sequence Diagram : Event Being Handled

Sequence Diagram : Event Being Handled

If Rendering Engine does not boot up because the event handler returns false, or there are no regions to be rendered, the screen is not rendered.

Sequence Diagram : Event Not Being Handled

Sequence Diagram : Event Not Being Handled

Handler

All rendering is done within the hander customized specifically for rendering called Rendering Handler.

The rendering handler is also a type of Handler and registered with the RegisterHanlder() function. The macro functions below are used to implement it.

Macro Description
HANDLER_DECLARE_VOIDRENDER Macro for declaration
HANDLER_IMPLEMENT_VOIDRENDER Macro for implementation

The argument of the rendering handler is an instance of the SFXGraphics class, which is used to render the screen.

Example : Registering and implementing the rendering handler
class MyWin : public SFRPlainWindow {
    public:
        explicit MyWin(Void) static_throws;
    private:
        HANDLER_DECLARE_VOIDRENDER(OnRender)
};

MyWin::MyWin(Void) static_throws : SFRPlainWindow(...)
{
    if (static_try()) {

        // register OnRender as rendering handler

        static_throw(RegisterHandler(SREVT_RESPONDER_RENDER,
                                     SRP16_RENDER_CONTENT, 
                                     HANDLER_BEFORE,
                                     HANDLER_FUNCTION(OnRender)));
    }
    return;
}

HANDLER_IMPLEMENT_VOIDRENDER(MyWin, OnRender, graphics)
{
    // use graphics instance to render within the rendering handler

    graphics->FillRectangle(GetContentWorld(), 
                               SFXRGBColor(0x00, 0xFF, 0xFF, 0x00));
    return;
}

Rendered Region

The rendered region should be registered if necessary. The actual rendering itself is done within the rendering handler.

For instance, if the text string of a label control is changed with the SetText() function, this label control must be rendered. In this case, register the rendering region in Rendering Engine using the InvalidateContent() function within the SetText() function.

When the control is moved to Rendering Engine, the registered regions are rendered all together.

Example : Registering the Rendered Region
class MyLabel : public SFRControl {
    public:
        explicit MyLabel(SFRResponderPtr director) static_throws;
        Void SetText(SFXAnsiStringConstRef param);
    private:
        HANDLER_DECLARE_VOIDRENDER(OnRender)
};

MyLabel::MyLabel(SFRResponderPtr director) static_throws
 : SFRControl(director, ...)
{
    if (static_try()) {

        // register OnRender as rendering handler

        static_throw(RegisterHandler(SREVT_RESPONDER_RENDER,
                                     SRP16_RENDER_CONTENT, 
                                     HANDLER_BEFORE,
                                     HANDLER_FUNCTION(OnRender)));
    }
    return;
}

Void MyLabel::SetText(SFXAnsiStringConstRef param)
{
    if (param != _text) {
        _text = param;

        // register in Rendering Engine  for rendering 

        InvalidateContent();
    }
    return;
}

HANDLER_IMPLEMENT_VOIDRENDER(MyLabel, OnRender, graphics)
{
    SFXRectangle rect(GetContentWorld());

    // render within the rendering handler

    graphics->FillRectangle(rect, SFXRGBColor(0xFF, 0xFF, 0xFF, 0x00));
    graphics->DrawString(_text, rect, SFXRGBColor(0x00, 0x00, 0x00, 0x00));
    return;
}

When the rendered region is registered, the rendering handler is called at the end of each event loop.

By using the overloaded InvalidateContent() function with a rectangle argument and limiting the rendered region, rendering performance can be improved.

Explicit Rendering

Since Rendering Engine boots up at the end of each event loop, Rendering Engine can not be booted up by a callback from outside the event loop, such as network callback or timer callback.

Sequence Diagram : Callback

Sequence Diagram : Callback

Rendering Engine needs to be explicitly booted up in order to render the screen after the network communication.

Sequence Diagram : Booting up Rendering Engine

Sequence Diagram : Booting up Rendering Engine

Rendering Engine can be explicitly booted up by sending the rendering event to the SFRApplication object. (Sending the rendering event to the Responder objects other than SFRApplication is not supported)

Example : Explicit Rendering
CALLBACK_IMPLEMENT_SFXHTTPCONNECTION(MyWin, OnConnect, error)
{
    SFRApplicationPtr app(SFRApplication::GetInstance());

    // change the text string of the label

    _label->SetText("OnConnect");

    // boot up Rendering Engine explicitly
    //
    // rendering does not occur without the code below 
    // and the result of the SetText() function cannot be observed 

    app->Invoke(SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, false));
    return;
}

Rendering Engine can be explicitly booted up by sending the SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, false) event.

* When the SFXEvent(SREVT_RESPONDER_RENDER, SRP16_RENDER_INVOKE, true) event is sent, all Responder objects are forcedly rendered by rendering handlers regardless of the registration of the rendering regions.