MacOS: Disable actions while file dialogs are open (#26169)

- Any action part of the application menu will trigger based on shortcut
  when a native file dialog is open
- Another way to fix this is to switch out the application menu, but
  afaict it requires writing native objective-c code. I think that's too
  much complexity just to get cmd+c/cmd+v in these file dialogs
- For now, just disable the actions so that select-all, rotate-left, etc
  don't trigger when pressed in these dialogs
- I've implemented an RAII wrapper to disable this. It should take
  pointers which should be fine because all of these dialog calls are
  blocking (so in principle nothing can change underneath).

I'm quite sure this won't have any adverse effects on other platforms,
but will need help from other developers to test in case.
This commit is contained in:
timpieces
2025-12-15 15:08:08 +08:00
committed by Chris Hennes
parent 9caa20b9eb
commit c575e63814

View File

@@ -30,6 +30,7 @@
#include <QGridLayout>
#include <QGroupBox>
#include <QLineEdit>
#include <QMenuBar>
#include <QPushButton>
#include <QRadioButton>
#include <QRegularExpression>
@@ -50,6 +51,45 @@
using namespace Gui;
// An raii-helper struct to disable actions while dialogs are open
// At least on macos, shortcuts for enabled actions will still trigger while dialogs are open
struct ActionDisabler
{
ActionDisabler()
{
auto mainWin = Gui::getMainWindow();
if (!mainWin) {
return;
}
QMenuBar* menuBar = mainWin->menuBar();
if (!menuBar) {
return;
}
auto actions = menuBar->actions();
actionsToReenable.reserve(actions.size());
for (auto action : actions) {
if (action->isEnabled()) {
action->setEnabled(false);
actionsToReenable.push_back(action);
}
}
}
~ActionDisabler()
{
for (auto action : actionsToReenable) {
if (action) {
action->setEnabled(true);
}
}
}
FC_DISABLE_COPY(ActionDisabler)
std::vector<QAction*> actionsToReenable {};
};
bool DialogOptions::dontUseNativeFileDialog()
{
#if defined(USE_QT_DIALOGS)
@@ -195,6 +235,8 @@ QString FileDialog::getSaveFileName(
Options options
)
{
ActionDisabler actionDisabler {};
QString dirName = dir;
bool hasFilename = false;
if (dirName.isEmpty()) {
@@ -239,6 +281,7 @@ QString FileDialog::getSaveFileName(
windowTitle = FileDialog::tr("Save As");
}
// NOTE: We must not change the specified file name afterwards as we may return the name of an
// already existing file. Hence we must extract the first matching suffix from the filter list
// and append it before showing the file dialog.
@@ -298,6 +341,7 @@ QString FileDialog::getExistingDirectory(
Options options
)
{
ActionDisabler actionDisabler {};
QString path = QFileDialog::getExistingDirectory(parent, caption, dir, options);
// valid path was selected
if (!path.isEmpty()) {
@@ -321,6 +365,7 @@ QString FileDialog::getOpenFileName(
Options options
)
{
ActionDisabler actionDisabler {};
QString dirName = dir;
if (dirName.isEmpty()) {
dirName = getWorkingDirectory();
@@ -385,6 +430,7 @@ QStringList FileDialog::getOpenFileNames(
Options options
)
{
ActionDisabler actionDisabler {};
QString dirName = dir;
if (dirName.isEmpty()) {
dirName = getWorkingDirectory();
@@ -799,6 +845,7 @@ void FileChooser::setFileName(const QString& fn)
*/
void FileChooser::chooseFile()
{
ActionDisabler actionDisabler {};
QString prechosenDirectory = lineEdit->text();
if (prechosenDirectory.isEmpty()) {
prechosenDirectory = FileDialog::getWorkingDirectory();