diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2023-10-24 17:09:13 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2023-11-12 18:40:03 +0200 |
commit | 0366b554c936e3257797f9c281c1cbd76e426dd3 (patch) | |
tree | a4d61889559ac67692d23aabc59921ecd7568282 /src/plugins/platforms/cocoa | |
parent | b49211c12543c3369b286b507c4277c66ef9fdef (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.mm | 52 |
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. |