Creating Custom Widgets

Introduction

Creating a custom widget is pretty simple in wxFormBuilder. You will need 4 things.

  1. An xml file defining the widget and its properties.
  2. A shared library (.dll or .so) which will construct an actual widget and pass it to the wxFormBuilder application.
  3. Extra code in the shared library to define XRC code generation (if desired).
  4. An xml file defining the C++ code generation (if desired).

This tutorial goes through a very simple example of how to create a MyButton custom control.

Steps

  1. Create a xml-descriptor
    See common.xml for example.
    XML-Descriptor: "own.xml"
    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
    <package name="own" lib="libown" icon="button16x16.xpm" desc="My own controls">
      <objectinfo class="MyButton" icon="button.xpm" type="widget">
        <inherits class="wxWindow"/>
        <property name="name" type="text">m_button</property>
        <property name="style" type="bitlist">
          <option name="wxMB_STYLE1"      help=""/>
          <option name="wxMB_STYLE2"      help=""/>
        </property>
        <property name="label" type="wxString_i18n">MyButton</property>
      </objectinfo>
    </package>

    Things to note:
    • The "lib" attribute of the root "package" element defines the name of the shared library from which to load the widget at runtime.
    • The value of the "lib" attribute should not have a file extension (.dll or .so), wxFB will determine that from the platform at runtime.
    • A debug build of wxFB will expect to find the same library with a "d" suffix. Example: libownd.dll
    • In this example, the "MyButton" class inherits all the properties of "wxWindow" by using the "inherits" element.

  2. Create the plug-in library
    • See common.cpp in plugins/common in the wxFB source tree for an example.
      Plugin Library: own.cpp
      #include <component.h>
      #include <plugin.h>
      #include <xrcconv.h>
      
      #include "mybutton.h"
      
      class MyButtonComponent : public ComponentBase
      {
      public:
      
      	wxObject* Create(IObject *obj, wxObject *parent)
      	{
      		return new MyButton((wxWindow*)parent,-1,
      			obj->GetPropertyAsString(_("label")),
      			obj->GetPropertyAsPoint(_("pos")),
      			obj->GetPropertyAsSize(_("size")),
      			obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
      	}
      };
      
      BEGIN_LIBRARY()
        WINDOW_COMPONENT("MyButton",MyButtonComponent)
        MACRO(wxMB_STYLE1)
        MACRO(wxMB_STYLE2)
      END_LIBRARY()
      

      Things to note:
      • This library will need to be compiled with the same compiler and settings as wxFB.
        • If you are using a released binary of wxFB then it is a Unicode build with gcc 3.4.5 on Windows and gcc 4.x on Ubuntu.
      • You will need the plugin_interface and tinyxml libraries. Both of which can be found here.
      • The include files are in sdk/plugin_interface.
        • You will need to either change the include statements to reflect this or add additional include directories when compiling.
      • This creates an actual "MyButton" object on the heap and returns a pointer to wxFB. wxFB uses real widgets.
      • IObject is an abstract interface to wxFB internal Object class. Use it to get the properties of your widget and its base classes (like wxWindow) from the property grid.
      • In version 3.0+ you can get parent and child objects for complicated widgets and also interact with the wxFB application with the IManager interface.
        • Use GetManager() to get an IManager*.
      • Look in component.h to see what methods are available for IObject and IManager.
      • The "BEGIN_LIBRARY" and "END_LIBRARY" macro pair build a list of available components to be loaded by wxFB.
      • The "WINDOW_COMPONENT" macro links the name of your widget, "MyButton", with the name of the class in the shared library, "MyButtonComponent".
        • There are also SIZER_COMPONENT and ABSTRACT_COMPONENT.
      • The "MACRO" macro links the name of a style flag with the actual value of the flag.
  3. Add code to your component class for XRC code generation (if desired)
    Override the ExportToXrc and ImportFromXrc virtual functions in your component class to support XRC code.
    class MyButtonComponent : public ComponentBase
    {
    public:
    
    	wxObject* Create(IObject *obj, wxObject *parent)
    	{
    		return new MyButton((wxWindow*)parent,-1,
    			obj->GetPropertyAsString(_("label")),
    			obj->GetPropertyAsPoint(_("pos")),
    			obj->GetPropertyAsSize(_("size")),
    			obj->GetPropertyAsInteger(_("style")) | obj->GetPropertyAsInteger(_("window_style")));
    	}
    
            TiXmlElement* ExportToXrc(IObject *obj)
    	{
    		ObjectToXrcFilter xrc(obj, _("MyButton"), obj->GetPropertyAsString(_("name")));
    		xrc.AddWindowProperties();
    		xrc.AddProperty(_("label"),_("label"),XRC_TYPE_TEXT);
    		return xrc.GetXrcObject();
    	}
    
    	TiXmlElement* ImportFromXrc(TiXmlElement *xrcObj)
    	{
    		XrcToXfbFilter filter(xrcObj, _("MyButton"));
    		filter.AddWindowProperties();
    		filter.AddProperty(_("label"),_("label"),XRC_TYPE_TEXT);
    		return filter.GetXfbObject();
    	}
    };
    

    Things to note:
    • If the widget that you are adding is not supported by the XRC standard it may be better to not add XRC support and let it be generated as "unknown" in the XRC file.
  4. Create a template for C++ code generation (if desired)
    See common.cppcode for example.
    C++ template: "own.cppcode"
    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
    <codegen language="C++">
      <templates class="MyButton">
        <template name="declaration">MyButton* $name;</template>
        <template name="construction">
           $name = new MyButton( #wxparent $name, $id, $label, $pos, $size, $style #ifnotnull $window_style @{ |$window_style @} );
        </template>
        <template name="include">@#include &lt;mybutton.h&gt;</template>
      </templates>
    </codegen>

    Things to note:
    • All of the properties of your object and of its base classes (like "wxWindow") are accessed by a dollar-sign ('$') and the property name.
    • The code for the properties will be generated based on the type of property.
      • Example: "wxString" properties generate quoted strings, "wxSize" properties generate wxSize objects, etc.
    • The '#' character indicates a macro used by the template parser. See src/codegen/codegen.h for a list and description of available macros.
    • The '@' character escapes other characters. Example: @# @$ @@
  5. Make wxFB aware of your plugin
    1. Put your shared library (.dll or .so), your .xml file, and your .cppcode file (if any) into the same directory.
    2. Put that directory in the plugins subdirectory of your wxFB directory (put it in bin/plugins if you have the entire source tree).
    3. wxFB scans for new plugins at launch.

You are done!