Thursday, April 21, 2016

Effective Enum Handling in UI

Enums in OpendTect have three different aspects:
  1. The enum value
  2. The Key string (mostly used in IOPar, also going to text files)
  3. The UiString as shown in the user interface, say in a uiComboBox.

To enable a convenient transformation from one aspect to another we have the class EnumDef which manifests itself in a derived template class  EnumDefImpl<ENUM>. For each enum type, the corresponding EnumDef should be declared in the header file using the macro mDeclareEnumUtils or mDeclareNameSpaceEnumUtils. See enums.h for details. For example:

class MyClass
{
    enum State    { Good, Bad, OK, Undef };
                   mDeclareEnumUtils(State)


Then in the source file we need to define the keys using the macro mDefineEnumUtils:

 mDefineEnumUtils(MyClass,State,"My class state")
    { "Good", "Bad", "OK", "Undefined", 0 }

The strings above are the keys that would be useful in an IOPar or while writing to a file. By default the same strings will also be used in the UI. But if you want to use a different set of strings in UI, you can optionally define the uiStrings:

template <>
void EnumDefImpl<MyClass::State>::init()

{
    uistrings_ += tr("Lovely");
    uistrings_ += tr("Terrible");
    uistrings_ += toUiString("OK");
    uistrings_ += tr("Not sure");  
}

To use them in the UI, you can directly pass the EnumDef to classes like uiComboBox and uiStringListInpSpec. To set the value you can use:

combobox_->setCurrentItem( MyClass::StateDef().indexOf(oldval) );

And to get the value from the UI as enum you can do:

MyClass::State inp = MyClass::StateDef().getEnumValForIndex(
                                combobox_->currentItem() );

While reading from or writing to an IOPar, you should always use the enum key and the functions:

    EnumDef::indexOf(const char*)
    EnumDef::getKeyForIndex(int)

If you want more customization for a particular UI class, you can make your own copy of the EnumDef and make modifications like changing the uiStrings, removing some of the enums etc. For example see uiprestackattrib.cc.

The bottom line is, we have a lot of tools available, but to keep the code safe we need to take care of the following:
  • Always use the enum type (and not their int value) as class members or function arguments.
  • In IOPar or text files always write the key strings (not the int values or strings derived from uiStrings).
  • For existing enum types you can modify the uiStrings but never change the key strings if you want to maintain any backward compatibility with old files.


No comments:

Post a Comment