New feature for GtkUIManager class

I’ve been working for several months in the development of the Modest email client. It was a very interesting work, using gtk deeply trying to improve some internal features of the Gtk UI Manager system.

Modest has lots of strict rules about dimming toolbar icons, menu options, action widgets, …. Most applications don’t have a well designed system to manage this kind of events and that behaviour is shared between several files and classes; sometimes these dimming rules are implemented several times, which causes many runtime errors as well as some problems to extend classes and use cases logic.

During my work in Modest I designed a system to manage dimming logic in Gtk applications. This design is based on GtkUIManager system and tries to solve the same problem: centralize and simplify UI events on a GtkWindow. The following UML diagram shows an example of this system:

UIDimmingManager class diagram

Implementation details

The next step should be adding this logic to the Gtk core in order to be part of the GtkUIManager system. It will not be very hard to add this behaviour to that class, because UIDimmingManager pattern has the same structure than the Gtk UIManager.

The classes involved in this design and responsibilities are described as follow:

  • UIDimmingManager
    • This class stores and handles UI Dimming Rules Groups . Each rules group has a string name, which will be used inside the manager to execute a specific group. This rules group is stored internally in a hash map; however, a different data structure could be used for that purpose.
    • The API of this class exports two different methods to execute rules:
      • ui_dimming_manager_process_dimming_rules: execute all rules groups.
      • ui_dimming_manager_process_dimming_rules_group: execute a specific rules group.
  • DimmingRulesGroup
    • Stores and handles a dimming rules group.
    • It could also manage two different types of dimming rules:
      • Common dimming rules: This kind of rules are defined for GtkUIManager items, so they have to be defined previously inside the UIManager structure.
      • Widget dimming rules: These rules can be applied to any widget.
    • Each dimming rule could have a notification dialog in order to inform the user why some item is dimmed. This notification system is enabled or disabled for all rules defined in a single group.
  • DimingRule
    • This class actually implements dimming rules behaviour.
    • It should receive three parameters at creation time:
      • Window: a GtkWindow or CustomWindow instance to apply dimming rule.
      • Callback: callback function for checking dimming behaviour.
      • Action path: the path to locate UIAction element, registered in UIManager.
        • This parameter is optional, because for widget dimming rules is not required.
  • UIDimmingRules
    • This element was defined as a plain file, with a list of operations to be used as dimming rules callback.
    • Its similar to the common file used in UIManager stock items implementation for defining actions for each item.

Once you have defined your UIManager structure, with your xml file for defining stock items and UI actions, you only have to define your dimming data structure, very similar to UIManager
structure:

/* Menu Dimming rules entries */
static const DimmingEntry menu_dimming_entries [] = {

/* Email Menu */
{ "/MenuBar/Menu1/Menu1Submenu1/Menu1Submenu1Item1", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu1/Menu1Submenu1/Menu1Submenu1Item2", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu1/Menu1Item3", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu1/Menu1Item4", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu1/Menu1Item5", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu1/Menu1Item6", G_CALLBACK(ui_dimming_rules_on_rule1) },

{ "/MenuBar/Menu2/Menu2Submenu1/Menu2Submenu1Item1", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu2/Menu2Submenu1/Menu2Submenu1Item2", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu2/Menu2Item3", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu2/Menu2Item4", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu2/Menu2Item5", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu2/Menu2Item6", G_CALLBACK(ui_dimming_rules_on_rule1) },

{ "/MenuBar/Menu3/Menu3Submenu1/Menu3Submenu1Item1", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu3/Menu3Submenu1/Menu3Submenu1Item2", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu3/Menu3Item3", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu3/Menu3Item4", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu3/Menu3Item5", G_CALLBACK(ui_dimming_rules_on_rule1) },
{ "/MenuBar/Menu3/Menu3Item6", G_CALLBACK(ui_dimming_rules_on_rule1) }
};