Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   Related Pages  

Lesson 7

Lesson7: Creating custom widget
In this lesson you will learn how to enhance widget so it suits your needs, and also how to create a new widget. We will start by creating an enhanced widget, then we will use it in existing application, so you could see how easy it is to update your code, once you decide to change something. And the most interesting part would be how to make our widget and make it work with already existing classes (you can not even imagine the simplicity of this process). We will modify code from Lesson 6.
Let us enhance a widget:
// We will start from enhancing CEdit class. Imagine that you want
// to limit user input only to digits. You can set text to whatever you need,
// but user can enter only digits
class CDigEdit: public CEdit
{
public:
    // All we need to do is to override OnKeyDown() function
    virtual void OnKeyDown(int &iKey)
    {
        // let's check if input is not a control key (like, right or left)
        if(iKey>31 && iKey<128)
            // now check if it is not a digit, just ignore it.
            if(iKey<48 || iKey>57)iKey=SJ_KEY_IGNORE;
    }
}; // end of CDigEdit class
Now how can we use it? Easy! Remember your first dialog from Lesson 6? There is only one change to make it use your enhanced widget:
// The only thing we change in this class from lesson6, is types
// of the first private member m_edt, the rest of the class stays
// absolutely the same. But now it is different
class CCustomTextDlg: public CDlg
{
    CDigEdit        m_edt; // edit field
public:
    // This string will have the result of user interaction 
    // and it could be accessed by calling class
    std::string m_str;
    CCustomTextDlg():CDlg()
    {
        SetAutoHide();              // Set autohide option 
        SetCaption("Enter text:");  // Change caption
        SetSize(250,90);            // Size of the dialog
        RegisterChild(&m_edt);      // Register edit field
        m_edt.SetText("sjgui");     // New text for edit field
    }
    // Place edit field in a proper position
    virtual void OnReshape()
    {
        // it should be under the caption
        m_edt.PosWnd(5,m_Caption.GetBottom()+1,GetWidth()-10,m_edt.GetHeight());
    }
    // React on closing the window, if it was Ok pressed then change the text
    virtual void OnComplete(){if(m_btnOk.IsPushed())m_str=m_edt.GetText();}
}; // end of CCustomTextDlg
Now let us add some fancy graphics. This is another widget:
// And let us create a new widget which is using 3D drawing
// Maybe you remember that in lesson2 we created a space window with 
// rotating triangle. Let us use it!
// This class is pretty much the same as CSpcWnd for lesson2.
// What we will do with this class? We can use it as any other widget, but we
// also could modify existing widget like button, for instance. 
// Widgets are created as templates, so that they can use different classes 
// as you need them. Why not use this new widget instead of label for the button? 
// Should we inherit our class from CLabel? It is the easiest and usual way, but 
// in gerenral all you need to do is to provide functions that are used by button, 
// which are SetText(),GetTextLen() and GetFontSize(). But for simplicity we will use
// base class CLabel
class CWig : public CLabel
{
    float   m_fRotation; // Current rotation angle
public:
    CWig(){m_fRotation=0.0f;} // constructor
    virtual void OnAnimate() // New handler for animation
    {
        // change rotation by 1 degree
        m_fRotation+=1;
        // we do not want to have rotation angle more then 360 degrees
        if(m_fRotation>360)m_fRotation-=360;
    }
    // Drawing is as it was in lesson2
    // There is no command for animation, because it is called in CSpcWnd
    virtual void OnDraw()
    {
        SET_GL_SIMPLE_PROSPECTIVE; // The same as it was in lesson2
        glTranslatef(0.0f,0.0f,-10.0f); // Move triangle down
        // Now we rotate our triangle around Y axis
        glRotatef(m_fRotation,0.0f,1.0f,0.0f);
        // Let us set color to a blue
        glColor3f(0.0f,0.0f,1.0f);
        // this is our triangle in the center of the screen.
        glBegin(GL_TRIANGLES);
            glVertex3f( 0.0f, 1.0f, 0.0f);
            glVertex3f(-1.0f,-1.0f, 0.0f);
            glVertex3f( 1.0f,-1.0f, 0.0f);
        glEnd();
        // We change setting, so we need to set them back
        SET_GL_FOR_GUI_DRAW;
    }
}; // end of CWig class
Once it is ready to use, look how you can make use of it:
// The only change from lesson6 is type of m_btn member
// CButton is a nice name for class CPushButtonTmpl<CLabel>, which is a button
// using simple label, but you can change it by giving your own class
class CSpcWnd : public CWnd
{
    CButtonTmpl<CWig>   m_btn; // Button widget
    CCustomTextDlg      m_dlg; // Dialog window
public:
    // We reset m_fRotation and also set our check box parameters.
    CSpcWnd():CWnd()
    {
        RegisterChild(&m_btn);          // Register button
        RegisterChild(&m_dlg);          // Register dialog
        m_btn.SetLabel("default text"); // Now we can set text of the button.
    }
    // We need to handle resizing of the window so button and dialog
    // would be properly positioned
    void virtual OnReshape()
    {
        // We should set size of the button accordingly to the length of the text
        m_btn.SetSize((m_btn.GetTextLen()+1)*m_btn.GetFontSize(),80);
        m_btn.ToCenter(); // button should be in the center
        m_dlg.ToCenter(); // dialog also should be in the center
    }
    // Key up event handler to show dialog or change button text
    void virtual OnKeyUp(int &iKey)
    {
        // check if we need to change the text
        if(m_dlg.IsOk())
        {
            m_btn.SetLabel(m_dlg.m_str.data()); // Set text
            // We need to call this function if we want button size
            // to be dynamically changed with the string.
            // And we call Reshape() instead of OnReshape(), because
            // we need child objects to be reshaped too!
            Reshape();
        }
        // Reset dialog status, so next time some key was pressed
        // we would not do the same thing again
        if(m_dlg.IsOk() || m_dlg.IsCancel())
            m_dlg.Reset();
        // Show dialog if button was pressed show or hide dialog
        if(m_btn.IsPushed())
        {
            m_btn.Reset(); // Important not to forget to reset the button!
            m_dlg.Show(!m_dlg.IsVisible()); // Switch dialog on or off
        }
    }
    // Drawing is very simple, we just need to set up the OpenGL projection mode
    // using predefined macro, and do not forget to Animate controls
    virtual void OnDraw(){Animate();SET_GL_FOR_GUI_DRAW;}
}; // end of CSpcWnd class
Some information about widgets.
There is a notation of the style for widgets, the default is sjgui::modern, if you want to use any other style you should define SJGUI_USE_STYLE with the name of the style. This is the list of all available Widget Styles.
If you want to change colors of the widgets, you may do that for each control individually. But when widget is created it uses default settings (it correspond not only to color but to the sizes also). So if you which change that value you just need to do so before you create element. These parameters are unique for each style. These are available default parameters and their values for sjgui::modern:
namespace modern
{
    // CWnd
    // Default minimum size of a control.
    CWnd::CSize     def_wnd_min_size(0,0);
    // CWndCtrl
    // Default CWndCtrl border light color.
    GLfloat def_wnd_ctrl_border_light_color[4]={1.0f,1.0f,1.0f,1.0f};
    // Default CWndCtrl border dark color.
    GLfloat def_wnd_ctrl_border_dark_color[4]={0.3f,0.3f,0.3f,0.3f};
    // Default CWndCtrl shadow color.
    GLfloat def_wnd_ctrl_shadow_color[4]={0.3f,0.3f,0.3f,0.5f};
    // Default CWndCtrl background color.
    GLfloat def_wnd_ctrl_bg_color[4]={0.3f,0.5f,0.7f,1.0f};
    // Default CWndCtrl background color when selection.
    GLfloat def_wnd_ctrl_bg_sel_color[4]={0.6f,0.5f,0.7f,1.0f};
    // CPushButtonTmpl and CCheckBoxTmpl
    // default button size
    CWnd::CSize     def_button_size(100,30);
    // CDlgTmpl
    // Default caption color
    GLfloat         def_dlg_caption_color[4]    ={0.1f,0.9f,1.0f,1.0f};
    // Default text color
    GLfloat         def_dlg_button_text_color[4]={0.1f,0.9f,1.0f,1.0f};
    // Default minimum size for dialog
    CWnd::CSize     def_dlg_min_size(240,50);
    // Default distance of the button from the edge
    int             def_dlg_btn_dist            =5;
    // Default distance between buttons
    int             def_dlg_btn_offset          =5;
    // Default height of the button panel
    int             def_dlg_btn_panel_height    =40;
    // CPlaneWnd
    // Default dlgwnd border color.
    GLfloat         def_dlg_wnd_border_color[4] ={0.5f,0.5f,0.5f,1.0f};
    // Default dlgwnd background color.
    GLfloat         def_dlg_wnd_bg_color[4]     ={0.1f,0.5f,0.5f,1.0f};
    // Default dlgwnd background color when selected.
    GLfloat         def_dlg_wnd_bg_sel_color[4] ={0.11f,0.51f,0.51f,1.0f};
    // CEditTmpl
    // Default font size for edit.
    int             def_edit_font_size          =10;
    // Default blink period for edit.
    int             def_edit_blink_period       =750;
    // Default minimum size of a control.
    CWnd::CSize     def_edit_size(100,12);
    // CLabel
    // Default label text color.
    GLfloat         def_label_color[4]          ={1.0f,1.0f,1.0f,1.0f};
    // Default label horizontal alignment.
    CWnd::eAligns   def_label_hor_aling         =CWnd::ALIGN_CENTER;
    // Default label vertical alignment.
    CWnd::eAligns   def_label_ver_aling         =CWnd::ALIGN_CENTER;
    // CSlider
    // Default slider size.
    CWnd::CSize     def_slider_size(16,16);
    // Default slider lengths.
    float           def_slider_length           =0.2f;
    // Default slider orientation.
    CWnd::eOrient def_slider_orientation=CWnd::HORIZONTAL;
    // CText
    // Default text color
    GLfloat         def_text_color[4]           ={1.0f,1.0f,1.0f,1.0f};
    // CTextBoxTmpl
    // Default text box text color.
    GLfloat         def_text_box_text_color[4]  ={0.8f,1.0f,0.8f,1.0f};
    // Default text box font size.
    int             def_text_box_fonst_size     =12;
    // Distance between edge of text field and sliders
    int             def_text_box_sl_dst         =0;
    // For clearing screen
    GLfloat         def_clear_color[4]={0.0f,0.0f,0.0f,1.0f};
} // end of namespace modern
Back to Lesson 6.
Now you are ready to go on your own, explore and create! I will be more then happy to hear about your experience with this library! Waiting for your comments. And for people who are not happy with glut and I can understand why :) go on to Lesson 8. Good-luck.
This is full source code:
// For this lesson, you will learn how to create a custom widget.
// to avoid useing "sjgui::" everywhere
using namespace sjgui;

// We will start from enhancing CEdit class. Imagine that you want
// to limit user input only to digits. You can set text to whatever you need,
// but user can enter only digits
class CDigEdit: public CEdit
{
public:
    // All we need to do is to override OnKeyDown() function
    virtual void OnKeyDown(int &iKey)
    {
        // let's check if input is not a control key (like, right or left)
        if(iKey>31 && iKey<128)
            // now check if it is not a digit, just ignore it.
            if(iKey<48 || iKey>57)iKey=SJ_KEY_IGNORE;
    }
}; // end of CDigEdit class

// And let us create a new widget which is using 3D drawing
// Maybe you remember that in lesson2 we created a space window with 
// rotating triangle. Let us use it!
// This class is pretty much the same as CSpcWnd for lesson2.
// What we will do with this class? We can use it as any other widget, but we
// also could modify existing widget like button, for instance. 
// Widgets are created as templates, so that they can use different classes 
// as you need them. Why not use this new widget instead of label for the button? 
// Should we inherit our class from CLabel? It is the easiest and usual way, but 
// in gerenral all you need to do is to provide functions that are used by button, 
// which are SetText(),GetTextLen() and GetFontSize(). But for simplicity we will use
// base class CLabel
class CWig : public CLabel
{
    float   m_fRotation; // Current rotation angle
public:
    CWig(){m_fRotation=0.0f;} // constructor
    virtual void OnAnimate() // New handler for animation
    {
        // change rotation by 1 degree
        m_fRotation+=1;
        // we do not want to have rotation angle more then 360 degrees
        if(m_fRotation>360)m_fRotation-=360;
    }
    // Drawing is as it was in lesson2
    // There is no command for animation, because it is called in CSpcWnd
    virtual void OnDraw()
    {
        SET_GL_SIMPLE_PROSPECTIVE; // The same as it was in lesson2
        glTranslatef(0.0f,0.0f,-10.0f); // Move triangle down
        // Now we rotate our triangle around Y axis
        glRotatef(m_fRotation,0.0f,1.0f,0.0f);
        // Let us set color to a blue
        glColor3f(0.0f,0.0f,1.0f);
        // this is our triangle in the center of the screen.
        glBegin(GL_TRIANGLES);
            glVertex3f( 0.0f, 1.0f, 0.0f);
            glVertex3f(-1.0f,-1.0f, 0.0f);
            glVertex3f( 1.0f,-1.0f, 0.0f);
        glEnd();
        // We change setting, so we need to set them back
        SET_GL_FOR_GUI_DRAW;
    }
}; // end of CWig class

// The only thing we change in this class from lesson6, is types
// of the first private member m_edt, the rest of the class stays
// absolutely the same. But now it is different
class CCustomTextDlg: public CDlg
{
    CDigEdit        m_edt; // edit field
public:
    // This string will have the result of user interaction 
    // and it could be accessed by calling class
    std::string m_str;
    CCustomTextDlg():CDlg()
    {
        SetAutoHide();              // Set autohide option 
        SetCaption("Enter text:");  // Change caption
        SetSize(250,90);            // Size of the dialog
        RegisterChild(&m_edt);      // Register edit field
        m_edt.SetText("sjgui");     // New text for edit field
    }
    // Place edit field in a proper position
    virtual void OnReshape()
    {
        // it should be under the caption
        m_edt.PosWnd(5,m_Caption.GetBottom()+1,GetWidth()-10,m_edt.GetHeight());
    }
    // React on closing the window, if it was Ok pressed then change the text
    virtual void OnComplete(){if(m_btnOk.IsPushed())m_str=m_edt.GetText();}
}; // end of CCustomTextDlg

// The only change from lesson6 is type of m_btn member
// CButton is a nice name for class CPushButtonTmpl<CLabel>, which is a button
// using simple label, but you can change it by giving your own class
class CSpcWnd : public CWnd
{
    CButtonTmpl<CWig>   m_btn; // Button widget
    CCustomTextDlg      m_dlg; // Dialog window
public:
    // We reset m_fRotation and also set our check box parameters.
    CSpcWnd():CWnd()
    {
        RegisterChild(&m_btn);          // Register button
        RegisterChild(&m_dlg);          // Register dialog
        m_btn.SetLabel("default text"); // Now we can set text of the button.
    }
    // We need to handle resizing of the window so button and dialog
    // would be properly positioned
    void virtual OnReshape()
    {
        // We should set size of the button accordingly to the length of the text
        m_btn.SetSize((m_btn.GetTextLen()+1)*m_btn.GetFontSize(),80);
        m_btn.ToCenter(); // button should be in the center
        m_dlg.ToCenter(); // dialog also should be in the center
    }
    // Key up event handler to show dialog or change button text
    void virtual OnKeyUp(int &iKey)
    {
        // check if we need to change the text
        if(m_dlg.IsOk())
        {
            m_btn.SetLabel(m_dlg.m_str.data()); // Set text
            // We need to call this function if we want button size
            // to be dynamically changed with the string.
            // And we call Reshape() instead of OnReshape(), because
            // we need child objects to be reshaped too!
            Reshape();
        }
        // Reset dialog status, so next time some key was pressed
        // we would not do the same thing again
        if(m_dlg.IsOk() || m_dlg.IsCancel())
            m_dlg.Reset();
        // Show dialog if button was pressed show or hide dialog
        if(m_btn.IsPushed())
        {
            m_btn.Reset(); // Important not to forget to reset the button!
            m_dlg.Show(!m_dlg.IsVisible()); // Switch dialog on or off
        }
    }
    // Drawing is very simple, we just need to set up the OpenGL projection mode
    // using predefined macro, and do not forget to Animate controls
    virtual void OnDraw(){Animate();SET_GL_FOR_GUI_DRAW;}
}; // end of CSpcWnd class

// Now we need to setup our window, we do it in the function main.
int main(int argc, char* argv[])
{
    // Events receiver
    CSpcWnd SpcWnd;
    // Position the window
    SpcWnd.PosWnd(20,50,320,240);
    // Create window
    if(Create(argv[0],&SpcWnd))
    {
        // Run it. Now all events are transferred to our class.
        GlobalMainLoop();
    }
    else
        // Print error message if something was wrong.
        printf("Could not create opengl window.\n");
    return 0;
} // the end of main
sjgui logo
Quick Links:

 News
 Description
 Screen Shots
 Projects
 Downloads
 Source Code
 Help/FAQ
 Want to help?
 Credits
 Disclaimer


Documentation:

 Documentation
 Reference
 Lessons


Useful links:

sjcomp logo
sjcomp

opengl logo

nehe logo

SourceForge.net Logo

Last modified:


Started by Alexander Shyrokov. Generated at Wed Apr 28 12:31:05 2004 for the sjgui by doxygen 1.3.1. SourceForge.net Logo