Dynamic Options/Preferences Dialog Library for wxWidgets

So I’ve been doing a lot of work related to my carputer lately, and one of my big focuses is getting Roadnav to be more touchscreen friendly so that I can use it as my primary GPS application. One of the biggest problems I’ve found with most applications (and Roadnav is no exception, unfortunately) is that most of the dialogs are simply too big for my screen. The preferences screen has especially stuck out as an offender here.

To solve this, over the last week I’ve created a set of generic classes that you can use in wxWidgets to create options/preferences dialogs that have a consistent feel to them, and can be easily created at runtime. Its setup in a hierarchical manner, and each layer of the menu is dynamically generated at runtime. Here’s a nice screen shot of the top level menu for your viewing pleasure.

Now, since I’m designing this for a touchscreen, right now the visual design is definitely touchscreen focused. However, its generic enough (thanks to wxWidgets sizers) that you can change the logic in the dialog to whatever you want it to be, without having to change the definition of your menus. In fact, if you do it right you can even switch the look at runtime because of all controls are created at runtime, so each one of the option classes is designed so that you can destroy its controls and then bring it back again.

Another cool option that could be implemented is loading the definitions for the options from an XML file or something similar — I’ve actually created something like that before in C# for the pGina project (see their SVN) — since everything is dynamic, it would be pretty trivial to do that.

Anyways, here’s an overview of how it works:

The core of this code is that there are two types of classes, OptionGroup and classes that derive from Option. Option groups are used to setup menus that can be traversed in the dialog. Each OptionGroup can either contain a set of option groups (which will be used to define a menu), or a set of options that will be used to define a set of options to be displayed.

To define your menu, there are a bunch of macros that make constructing an option menu much easier/cleaner and intuitive, and allows you to easily setup the menus in a hierarchical manner. Example code for defining the options using the macros:

OPTIONS_START(rootOptionGroup)
    DEF_OPTION_GROUP(_("Group1"))

        DEF_OPTION_GROUP(_("Inner group1"))
            DEF_COMBOBOX_ITEMS(g_config, wxT("ConfigValue1"), _("Description"), wxT("DefaultValue"))
                DEF_COMBOBOX_ITEM(wxT("DefaultValue"))
                DEF_COMBOBOX_ITEM(wxT("Item1"))
                DEF_COMBOBOX_ITEM(wxT("Item2"))
            END_COMBOBOX_ITEMS
        END_OPTION_GROUP

        DEF_OPTION_GROUP(_("Inner group2"))
            DEF_BOOL_OPTION(g_config, wxT("ConfigValue2"), _("Description"), true)
        END_OPTION_GROUP
    END_OPTION_GROUP
END_OPTIONS

After setting up this, then you need to pass the defined option groups (rootOptionGroup) to the  OptionsDialog. There are many option types (boolean, integer, string, file selection, etc) already built in, each of which derives from the Option class. If you need to define some custom types, creating a new option  is very trivial. To illustrate, here’s the definition for the string option type:

class stringOption : public Option {
    public:
        stringOption(wxConfigBase* config, const wxString& configValueName, const wxString& description, const wxString& defaultValue) : Option(config, configValueName, description )
        { m_defaultValue = config->Read(configValueName, defaultValue); };

        virtual void Display(wxFlexGridSizer * sizer, wxWindow * parent)
        {
            sizer->Add(new wxStaticText(parent, wxID_ANY, m_description));
            sizer->Add(m_textCtrl = new wxTextCtrl(parent, wxID_ANY, m_defaultValue));
        };

        void OnWindowDestroy() { m_defaultValue = m_textCtrl->GetValue(); };
        bool Save() { return m_config->Write(m_configValueName, m_defaultValue); };
        virtual bool Enable(bool enable) { return m_textCtrl->Enable(enable); };

    protected:
        wxString m_defaultValue;
        wxTextCtrl * m_textCtrl;
};

As you can see, its pretty trivial. The constructor saves information needed to display the option. Display() is called when its time to display the option, and you just add the controls to a grid sizer that has two columns in it. Whenever your option is going to be destroyed, then OnWindowDestroy() is called. Save() saves the data to a wxConfigBase class, and Enable() enables/disables the controls when needed.

This is GPL licensed code, and you can download the source code as always from my software page. There is also a Windows executable that you can download (available in a separate package) if you don’t want to compile the code yourself.

The code is designed to work on wxWidgets 2.6+ (haven’t fully tested 2.6 yet, however). I built this with Visual Studio 2005 and wxWidgets 2.8.7, and there is a VS 2005 project included, but your mileage may vary with that.

Download the source code

Leave a Reply