The various platforms supported by Qt have different guidelines for the order of buttons in dialogs. The most obvious issue is that the "OK" and "Cancel" buttons should be swapped on Mac OS X and GNOME, but there are several other differences as well. To ensure that dialogs created with Qt or Qt Designer look native on all window systems, Qt 4.2 introduces the QDialogButtonBox class that abstracts the button row usually located at the bottom or on the right side of dialogs.
The QDialogButtonBox class is a QWidget subclass that you can populate with a set of buttons and insert in your dialog's layout. By default, QDialogButtonBox lays out its buttons horizontally, but this can be changed by passing Qt::Vertical to the constructor.
When adding buttons to a QDialogButtonBox, we also pass a "button role" that specifies how the button should be handled. For example:
box->addButton(tr("OK"), QDialogButtonBox::AcceptRole); box->addButton(tr("Cancel"),QDialogButtonBox::RejectRole); box->addButton(tr("Apply"), QDialogButtonBox::ApplyRole); box->addButton(tr("Reset"), QDialogButtonBox::ResetRole); box->addButton(tr("Help"), QDialogButtonBox::HelpRole);
QDialogButtonBox uses the roles to order the buttons correctly with respect to the user's window system or desktop environment. The table below explains the available roles.
Role | Description | Examples |
---|---|---|
AcceptRole | Accepts the dialog | OK, Open, Save |
RejectRole | Rejects the dialog | Cancel, Close |
DestructiveRole | Risky way of closing the dialog | Discard, Don't Save |
ActionRole | Performs an action on the dialog without closing it | Find Next, More Info |
ResetRole | A "reset"-like action | Reset, Restore Defaults |
ApplyRole | An "apply"-like action | Apply, Try |
HelpRole | Invokes help | Help |
YesRole | Positive answer to a yes/no question | Yes, Yes to All |
NoRole | Negative answer to a yes/no question | No, No to All |
As of Qt 4.2, the class supports four "layout styles": Windows, Mac OS X, KDE, and GNOME. The buttons are ordered according to the current layout style (specified by the active QStyle) and to their roles. If there are several buttons with the same role, the relative order in which they were inserted is preserved in the final dialog. Thus, on Windows, the buttons are organized as follows:
(In Windows and Mac OS X styles, YesRole and NoRole are treated the same as AcceptRole and RejectRole, respectively.) On Mac OS X, the order is normally as follows:
Notice how the Help button is pushed to the far left, and how the dialog's accept button (e.g., OK) is pushed to the right. What the diagram doesn't show is that if there is more than one accept button, the extra ones are put to the left of the reject button, as specified in Apple Human Interface Guidelines.
Some buttons, such as OK, Cancel, and Help, occur over and over again in dialogs. For these, QDialogButtonBox provides convenience enum values that can be used instead of specifying a text and a role. For example:
box->addButton(QDialogButtonBox::Ok); box->addButton(QDialogButtonBox::Cancel); box->addButton(QDialogButtonBox::Apply); box->addButton(QDialogButtonBox::Reset); box->addButton(QDialogButtonBox::Help);
An alternative to calling addButton() for every button is to set the standardButtons property (which also shows up in Qt Designer):
box->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Apply | QDialogButtonBox::Reset | QDialogButtonBox::Help);
In Qt 4.2, the QMessageBox class has been rewritten to use QDialogButtonBox to display its buttons. The API has also been revised so that it supports the same standard buttons and button roles as QDialogButtonBox. As a result, QMessageBox's buttons are now ordered correctly on the different platforms.
The new QMessageBox also features a more native look on Mac OS X, a more flexible API that caters for message boxes with more than three buttons, and more sensible word-wrapping behavior.
In this section, we will review the source code of a MailEditor dialog class that uses QDialogButtonBox and QMessageBox. The screenshots below show the MailEditor dialog on Windows XP and Mac OS X.
Let's start with the constructor:
MailEditor::MailEditor(QWidget *parent) : QDialog(parent) { ... sendNowButton = new QPushButton(tr("Send Now")); ... saveDraftButton = new QPushButton(tr("Save Draft")); sendNowButton->setDefault(true); buttonBox = new QDialogButtonBox; buttonBox->addButton(sendNowButton, QDialogButtonBox::AcceptRole); buttonBox->addButton(discardDraftButton, QDialogButtonBox::DestructiveRole); buttonBox->addButton(sendLaterButton, QDialogButtonBox::RejectRole); buttonBox->addButton(saveDraftButton, QDialogButtonBox::ActionRole); QGridLayout *layout = new QGridLayout; ... layout->addWidget(buttonBox, 3, 0, 1, 2); setLayout(layout); }
We instantiate the QPushButtons as usual, but instead of inserting them in a QHBoxLayout, we put them in a QDialogButtonBox. The table below lists the buttons, the roles we chose for them, and the reasons for that choice.
Button | Role | Rationale |
---|---|---|
Send Now | AcceptRole | This button is the standard way of closing the dialog. |
Discard Draft | DestructiveRole | This button closes the dialog and may result in data loss. |
Send Later | RejectRole | This button closes the dialog but does not send the email. It is the equivalent of pressing Esc. |
Save Draft | ActionRole | This button doesn't close the dialog. |
In the sendNow() slot, we call QDialog::accept() to close the dialog with an "accepted" result:
void MailEditor::sendNow() { ... accept(); }
In discardDraft() and sendLater(), we call QDialog::reject() to close the dialog with a "rejected" result:
void MailEditor::discardDraft() { ... reject(); } void MailEditor::sendLater() { saveDraft(); reject(); }
The dialog's result value is accessible through QDialog::result(). Unlike the other three slots, the saveDraft() slot doesn't close the dialog:
void MailEditor::saveDraft() { ... textEdit->document()->setModified(false); }
If the user closes the dialog while there are unsaved changes, we show a message box:
void MailEditor::closeEvent(QCloseEvent *event) { if (textEdit->document()->isModified()) { int r = QMessageBox::warning(this, tr("Mail Editor"), tr("Do you want to save the draft " "before closing?"), QMessageBox::Save | QMessageBox::DontSave | QMessageBox::Cancel); if (r == QMessageBox::Save) { saveDraft(); event->accept(); } else if (r == QMessageBox::DontSave) { event->accept(); } else { event->ignore(); } } }
Notice that we give meaningful labels to the message box's buttons, namely Save, Don't Save, and Cancel. For the user, this is much less error-prone than the traditional Yes and No choice.
Copyright © 2006 Trolltech | Trademarks |