[202_106]: fix Cmd+V paste not working in macOS save file dialog#2901
[202_106]: fix Cmd+V paste not working in macOS save file dialog#2901divyansharma001 wants to merge 1 commit intoMoganLab:mainfrom
Conversation
Temporarily disable QAction shortcuts while a native file dialog is open on macOS, then restore them after the dialog closes. This prevents menu bar NSMenuItem key equivalents from intercepting Cmd+V/C/X/A before the dialog's text field can handle them. Fixes: MoganLab#2894
There was a problem hiding this comment.
Pull request overview
Fixes macOS native file dialog text-field editing shortcuts (Cmd+V/C/X/A) being intercepted by the app’s menu QAction shortcuts by temporarily clearing those shortcuts during QFileDialog::exec() and restoring them afterward.
Changes:
- Add macOS-specific logic in
qt_chooser_widget_rep::perform_dialog()to save/clear QAction shortcuts before showing the native file dialog and restore them after it closes. - Add developer note (
devel/202_106.md) documenting rationale and manual test steps.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/Plugins/Qt/qt_chooser_widget.cpp |
Temporarily clears menu QAction shortcuts around native file dialog execution on macOS. |
devel/202_106.md |
Documents the issue, rationale, and testing procedure for the fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #ifdef Q_OS_MACOS | ||
| // Restore menu shortcuts after the native dialog is closed | ||
| for (const auto& pair : savedShortcuts) { | ||
| pair.first->setShortcut (pair.second); |
There was a problem hiding this comment.
The restore loop assumes every QAction* in savedShortcuts is still alive. While a native modal dialog is open on macOS, it’s possible for actions/windows to be destroyed (e.g., via menu interactions), which would leave dangling pointers here and can crash. Consider storing QPointer<QAction> (or tracking destroyed) and skipping entries whose action has been deleted before restoring.
| pair.first->setShortcut (pair.second); | |
| QAction* action = pair.first; | |
| if (!action) | |
| continue; | |
| action->setShortcut (pair.second); |
| // intercept standard editing shortcuts (Cmd+V, Cmd+C, Cmd+X, Cmd+A) before | ||
| // the dialog's text field can handle them. Temporarily clearing all QAction | ||
| // shortcuts allows the native dialog to process these keys normally. | ||
| QList<QPair<QAction*, QKeySequence>> savedShortcuts; | ||
| QWidget* mainWin= QApplication::activeWindow (); | ||
| if (mainWin) { | ||
| for (QAction* action : mainWin->findChildren<QAction*> ()) { | ||
| if (!action->shortcut ().isEmpty ()) { | ||
| savedShortcuts.append (qMakePair (action, action->shortcut ())); |
There was a problem hiding this comment.
This loop clears shortcuts for all actions under the active window, not just the standard editing shortcuts called out in the PR description. That can temporarily disable other important app/window shortcuts while the dialog is open. Consider limiting the clearing/restoring to only the conflicting sequences (Copy/Paste/Cut/SelectAll, and optionally Undo/Redo) to reduce user-visible side effects.
| // intercept standard editing shortcuts (Cmd+V, Cmd+C, Cmd+X, Cmd+A) before | |
| // the dialog's text field can handle them. Temporarily clearing all QAction | |
| // shortcuts allows the native dialog to process these keys normally. | |
| QList<QPair<QAction*, QKeySequence>> savedShortcuts; | |
| QWidget* mainWin= QApplication::activeWindow (); | |
| if (mainWin) { | |
| for (QAction* action : mainWin->findChildren<QAction*> ()) { | |
| if (!action->shortcut ().isEmpty ()) { | |
| savedShortcuts.append (qMakePair (action, action->shortcut ())); | |
| // intercept standard editing shortcuts (Cmd+V, Cmd+C, Cmd+X, Cmd+A, etc.) | |
| // before the dialog's text field can handle them. Temporarily clearing | |
| // shortcuts only for these standard editing sequences allows the native | |
| // dialog to process them normally while preserving other application | |
| // shortcuts. | |
| QList<QPair<QAction*, QKeySequence>> savedShortcuts; | |
| QWidget* mainWin= QApplication::activeWindow (); | |
| if (mainWin) { | |
| const QList<QKeySequence> conflictingSequences = { | |
| QKeySequence::Copy, | |
| QKeySequence::Paste, | |
| QKeySequence::Cut, | |
| QKeySequence::SelectAll, | |
| QKeySequence::Undo, | |
| QKeySequence::Redo | |
| }; | |
| for (QAction* action : mainWin->findChildren<QAction*> ()) { | |
| const QKeySequence shortcut = action->shortcut (); | |
| if (!shortcut.isEmpty () && conflictingSequences.contains (shortcut)) { | |
| savedShortcuts.append (qMakePair (action, shortcut)); |
| #ifdef Q_OS_MACOS | ||
| // On macOS, QAction shortcuts registered in the menu bar become NSMenuItem | ||
| // key equivalents. When a native file dialog is open, these key equivalents |
There was a problem hiding this comment.
This function already uses OS_MACOS earlier, but the new shortcut-disabling logic is guarded with Q_OS_MACOS. Mixing platform macros in the same file makes it harder to reason about which builds include the code and can lead to accidental omissions in some configurations. Consider using the same macOS guard consistently (whichever is standard for this module).
What
Fix standard editing shortcuts (Cmd+V, Cmd+C, Cmd+X, Cmd+A) not working in
the native file dialog on macOS.
Why
On macOS, QAction shortcuts registered in the menu bar are converted to
NSMenuItem key equivalents. When a native file dialog (NSSavePanel/NSOpenPanel)
is shown, these key equivalents intercept standard editing shortcuts before
the dialog's text field can handle them — so pressing Cmd+V triggers Mogan's
"Paste" action instead of pasting into the filename field.
How
Temporarily disable all QAction shortcuts before
dialog->exec()and restorethem after the dialog closes. This allows the native dialog to process
Cmd+V/C/X/A through the standard Cocoa responder chain.
How to test
Fixes: #2894