summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-10-24 17:09:13 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-11-12 18:40:03 +0200
commit0366b554c936e3257797f9c281c1cbd76e426dd3 (patch)
treea4d61889559ac67692d23aabc59921ecd7568282 /src/plugins/platforms/cocoa
parentb49211c12543c3369b286b507c4277c66ef9fdef (diff)
macOS: Respect QPlatformDialogHelper::ButtonLayout in native alerts
The buttons in QMessageDialogOptions do not have any order that we can rely on. The standard buttons is just a bit mask, so any ordering done on the QMessageBox side is lost. The custom buttons are ordered in the same order the user added them, but this is not really the order we want them to appear in the dialog either, as we have a well defined order between roles provided by QPlatformDialogHelper. We now follow the QPlatformDialogHelper::ButtonLayout order for macOS, using the same heuristics for multiple Accept role buttons as QDialogButtonBox. For buttons with the same role, QMessageBox will respect the order they were added in, but this is not something we can do for the standard buttons, as long as they are flattened to a bit mask. Pick-to: 6.6 6.5 Change-Id: I401f202a7c2d83dc253e988531ad145624c97580 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r--src/plugins/platforms/cocoa/qcocoamessagedialog.mm52
1 files changed, 49 insertions, 3 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoamessagedialog.mm b/src/plugins/platforms/cocoa/qcocoamessagedialog.mm
index 82a4c90c23..993e645a67 100644
--- a/src/plugins/platforms/cocoa/qcocoamessagedialog.mm
+++ b/src/plugins/platforms/cocoa/qcocoamessagedialog.mm
@@ -151,6 +151,8 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
// button). If an explicit default or escape button has been set, we respect these,
// and otherwise we fall back to role-based default and escape buttons.
+ qCDebug(lcQpaDialogs).verbosity(0) << "Adding button" << title << "with" << role;
+
if (!defaultButton && role == AcceptRole)
defaultButton = tag;
@@ -186,19 +188,63 @@ bool QCocoaMessageDialog::show(Qt::WindowFlags windowFlags, Qt::WindowModality w
button.tag = tag;
};
+ // Resolve all dialog buttons from the options, both standard and custom
+
+ struct Button { QString title; int identifier; ButtonRole role; };
+ std::vector<Button> buttons;
+
const auto *platformTheme = QGuiApplicationPrivate::platformTheme();
if (auto standardButtons = options()->standardButtons()) {
for (int standardButton = FirstButton; standardButton <= LastButton; standardButton <<= 1) {
if (standardButtons & standardButton) {
auto title = platformTheme->standardButtonText(standardButton);
- addButton(title, standardButton, buttonRole(StandardButton(standardButton)));
+ buttons.push_back({
+ title, standardButton, buttonRole(StandardButton(standardButton))
+ });
}
}
}
-
const auto customButtons = options()->customButtons();
for (auto customButton : customButtons)
- addButton(customButton.label, customButton.id, customButton.role);
+ buttons.push_back({customButton.label, customButton.id, customButton.role});
+
+ // Sort them according to the QPlatformDialogHelper::ButtonLayout for macOS
+
+ // The ButtonLayout adds one additional role, AlternateRole, which is used
+ // for any AcceptRole beyond the first one, and should be ordered before the
+ // AcceptRole. Set this up by fixing the roles up front.
+ bool seenAccept = false;
+ for (auto &button : buttons) {
+ if (button.role == AcceptRole) {
+ if (!seenAccept)
+ seenAccept = true;
+ else
+ button.role = AlternateRole;
+ }
+ }
+
+ std::vector<Button> orderedButtons;
+ const int *layoutEntry = buttonLayout(Qt::Horizontal, ButtonLayout::MacLayout);
+ while (*layoutEntry != QPlatformDialogHelper::EOL) {
+ const auto role = ButtonRole(*layoutEntry & ~ButtonRole::Reverse);
+ const bool reverse = *layoutEntry & ButtonRole::Reverse;
+
+ auto addButton = [&](const Button &button) {
+ if (button.role == role)
+ orderedButtons.push_back(button);
+ };
+
+ if (reverse)
+ std::for_each(std::crbegin(buttons), std::crend(buttons), addButton);
+ else
+ std::for_each(std::cbegin(buttons), std::cend(buttons), addButton);
+
+ ++layoutEntry;
+ }
+
+ // Add them to the alert in reverse order, since buttons are added right to left
+ for (auto button = orderedButtons.crbegin(); button != orderedButtons.crend(); ++button)
+ addButton(button->title, button->identifier, button->role);
// If we didn't find a an explicit or implicit default button above
// we restore the AppKit behavior of making the first button default.