Using the XmlUi library

Introduction

The ToolStrip widget is typically used in place of the main window's menu bar. However, it can also be used in dialog windows and other places, just like a regular toolbar. It usually contains one or more sections with tool buttons in them. Large tool buttons have text below the 22x22 icon; small tool buttons are displayed in a grid of two rows and have a 16x16 icon with text on the right. It is also possible to display small rows of icons without any text. Sections automatically collapse when there is not enough room for them; in that case a chevron is displayed with a popup menu containing the actions from collapsed sections.

Actions can also be placed directly in the ToolStrip without using any sections. In that case they form a single row, similar to a traditional toolbar. This is especially useful when placing the tool strip in a dialog box. In addition, the ToolStrip can display an optional header widget on the left, and one or more auxiliary actions in the upper right corner.

XmlUi also contains two classes for managing the layout of actions. The Builder is associated with a window and populates the tool strips and contextual menus. The Client is a container for a single component's actions; it also stores the layout of actions in the tool strip and menus. Clients can be dynamically added and removed from the builder. The layout of actions defined by all clients is merged into a single structure.

Example

In the demo application, there is a main window and a text view, both providing their own sets of actions. Both inherit the XmlUi::Client class using multiple inheritance, so that they can be 'plugged' into the builder. The main window also creates an XmlUi::Builder object and registers the tool strip in it.

In the constructor of the main window, first the actions are initialized:

    action = new QAction( IconLoader::icon( "new" ), tr( "New" ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( newFile() ) );
    setAction( "newFile", action );

The setAction method adds the action to the client and assigns it an identifier.

Then captions for sections are defined:

    setTitle( "sectionFile", tr( "File" ) );

Storing captions in code instead of the XML file makes it easier to translate the application.

In the next step, the XML file describing the layout of the main window's actions is loaded (see Format of the XML files):

    loadXmlUiFile( ":/resources/mainwindow.xml" );

Then the XmlUi::ToolStrip widget is created and placed in the top area of the main window, usually occupied by the menu bar:

    XmlUi::ToolStrip* strip = new XmlUi::ToolStrip( this );
    setMenuWidget( strip );

When the main client is initialized, the builder can be created and the client can be plugged into it:

    XmlUi::Builder* builder = new XmlUi::Builder( this );
    builder->registerToolStrip( "stripMain", strip );
    builder->addClient( this );

This populates the tool strip with actions according to the layout decribed in the XML file.

The text view also inherits the XmlUi::Client class and initializes its own actions, section titles and layout in a similar way in its constructor. When the main window creates a view, it adds it to the builder:

    TextView* view = new TextView( this );
    setCentralWidget( view );

    builder()->addClient( view );

Actions provided by the view are automatically inserted at appropriate locations into the tool strip. When the view is deleted, it is automatically removed from the builder, so its UI elements are also automatically deleted.

Popup menus

The most common way of adding popup menus to tool buttons in the tool strip is to place a small arrow next to the tool button. When the arrow is clicked, the popup menu is expanded. When the button is clicked, the default action from the menu is executed automatically. This corresponds to the QToolButton::MenuButtonPopup mode of the tool button.

The text view uses this technique for the Save button. When clicked, the default Save action is performed. However, when the arrow is clicked, a menu is displayed with both Save and Save As actions.

    action = new QAction( IconLoader::icon( "save" ), tr( "Save" ), this );
    setAction( "popupSaveFile", action );

    action = new QAction( IconLoader::icon( "save" ), tr( "&Save" ), this );
    action->setShortcut( QKeySequence::Save );
    connect( action, SIGNAL( triggered() ), this, SLOT( saveFile() ) );
    setAction( "saveFile", action );

    action = new QAction( IconLoader::icon( "save-as" ), tr( "Save &As..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( saveFileAs() ) );

The first action defines the button and the next two actions are the popup menu items. You can attach the menu to the button using a single function call:

    setPopupMenu( "popupSaveFile", "menuSaveFile", "saveFile" );

Here, popupSaveFile is the identifier of the button, menuSaveFile is the identifier of the popup menu and saveFile is the identifier of the default action. Note that the first action is not connected to any slot, because the button will automatically invoke the default action when clicked.

Note that the key shortcut is automatically displayed in the tooltip of tool buttons. This also works in case of buttons which have an associated popup menu; in that case the shortcut of the default action is displayed.

You can also place tool buttons using the QToolButton::InstantPopup or QToolButton::DelayedPopup in the tool strip. The easiest way to do that is to create the action using the XmlUi::ToolStripAction class and use the setPopupMode method to set the appropriate popup mode. You will also have to manually attach the popup menu to the action.

Note that when a button with QToolButton::InstantPopup is triggered using the key shortcut, its popup menu is not displayed, but the action's slot is executed instead. To work around this, simply call execMenu method of the tool strip in the slot connected to this action. This ensures that the menu is placed correctly and the button is animated.

Context menus

The builder can create context menus that can be used by any client. For example, the text view displays a customized context menu when right mouse button is clicked:

void TextView::contextMenu( const QPoint& pos )
{
    QMenu* menu = builder()->contextMenu( "menuText" );
    if ( menu )
        menu->exec( mapToGlobal( pos ) );
}

Note that the context menus are deleted every time the UI is rebuilt (for example, when a client is added or removed from the builder). If you have to store the pointer returned by contextMenu (for example to pass it to a QSystemTrayIcon), you must connect to the reset signal of the builder and retrieve a new menu object in the associated slot.

Auxiliary actions

The tool strip widget can display several auxiliary actions in the top right corner. They are always displayed as small icons and cannot be defined using the XML file. To add an auxiliary action, simply call addAuxiliaryAction. Use the clearAuxiliaryActions method to remove all auxiliary actions. Auxiliary actions can also be placed in the small tool strip (without any sections).

You can also place a widget in the leftmost part of the tool strip using the setHeaderWidget method.

Using with Qt Designer

Creating actions manually in code is not always comfortable. For example, if you use the Qt Designer, it's much easier to use it to define the actions.

You can create components using the Qt Designer and still use the XmlUi classes. Define the actions as usual, but don't create any menus or toolbars in the designer. Instead, put the following code just after the call to the setupUi method generated by the UI compiler:

    foreach ( QAction* action, findChildren<QAction*>() )
        addAction( action->objectName(), action );

This code iterates over all actions belonging to the window and adds them to the XmlUi client using their object names as identifiers. You can also use the same method if you have existing code which creates many actions with appropriate object names.

Custom styles for Windows and Mac OS X

The XmlUi library provides custom visual styles for both Windows and Mac OS X. The XmlUi::WindowsStyle overrides the rendering of the menus and toolbar buttons to give them a more modern look. In also ensures the correct appearance of the tool strip labels and separators. The XmlUi::MacStyle improves the rendering of flat toolbar buttons which look ugly when using the standard Mac OS X style provided by Qt.

By default the XmlUi library provides a static plug-in for one of these styles, depending on the platform. To use the custom style, simply add the following code during the initialization of the application:

#if defined( Q_WS_WIN )
    QApplication::setStyle( "XmlUi::WindowsStyle" );
#elif defined( Q_WS_MAC )
    QApplication::setStyle( "XmlUi::MacStyle" );
#endif

Define the XMLUI_NO_STYLE_PLUGIN macro to disable these plug-ins. In that case the style can still be created manually. Define XMLUI_EXPORT_STYLE_PLUGIN when you compile XmlUi as shared a library, so that the appropriate plug-ins are exported.

When using the Windows style, a gradient is drawn in the background of the entire main window (except for the area occupied by the status bar). To use a similar gradient in other places, for example in dialog windows, you can use the XmlUi::GradientWidget. When not using the custom Windows style, it is drawn using white background.