From a2dabd42e010a36292ba3824f4c29de6397bdf05 Mon Sep 17 00:00:00 2001 From: timpieces Date: Mon, 15 Dec 2025 15:08:08 +0800 Subject: [PATCH] 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. --- src/Gui/FileDialog.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/Gui/FileDialog.cpp b/src/Gui/FileDialog.cpp index 3f7ca535a8..778b1d3b5c 100644 --- a/src/Gui/FileDialog.cpp +++ b/src/Gui/FileDialog.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -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 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();