diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 2afde49fe..5ff93f527 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -596,6 +596,11 @@ void DatabaseWidget::expireSelectedEntries() void DatabaseWidget::deleteSelectedEntries() { + // Prevent deletion when a modal dialog (e.g., file save dialog) is active + if (QApplication::activeModalWidget()) { + return; + } + const QModelIndexList selected = m_entryView->selectionModel()->selectedRows(); if (selected.isEmpty()) { return; @@ -1115,6 +1120,11 @@ void DatabaseWidget::cloneGroup() void DatabaseWidget::deleteGroup() { + // Prevent deletion when a modal dialog is active + if (QApplication::activeModalWidget()) { + return; + } + Group* currentGroup = m_groupView->currentGroup(); Q_ASSERT(currentGroup && canDeleteCurrentGroup()); if (!currentGroup || !canDeleteCurrentGroup()) { diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 4c9566929..b329ec499 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -2472,6 +2473,68 @@ void TestGui::testMenuActionStates() QVERIFY(isActionEnabled("actionPasswordGenerator")); } +void TestGui::testDeleteEntryDuringModalDialog() +{ + // Delete key in native file dialogs on macOS + // should not delete password entries + + // Add canned entries for consistent testing + addCannedEntries(); + + auto* entryView = m_dbWidget->findChild("entryView"); + auto* entryDeleteAction = m_mainWindow->findChild("actionEntryDelete"); + + // Count initial entries + int initialEntryCount = entryView->model()->rowCount(); + QVERIFY(initialEntryCount > 0); + + // Select the first entry + clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton); + entryView->setFocus(); + QApplication::processEvents(); + + // Create and show a modal dialog to simulate the file save dialog + QDialog modalDialog(m_mainWindow.data()); + modalDialog.setModal(true); + + // Use a timer to trigger the delete action while modal dialog is shown + bool deleteTriggered = false; + QTimer::singleShot(50, [&]() { + // Verify modal dialog is active + QVERIFY(QApplication::activeModalWidget() == &modalDialog); + + // Trigger delete action while modal is open + entryDeleteAction->trigger(); + deleteTriggered = true; + + // Close the modal dialog + modalDialog.accept(); + }); + + // Show modal dialog (blocks until closed) + modalDialog.exec(); + + QVERIFY(deleteTriggered); + QApplication::processEvents(); + + // Verify entry count unchanged - delete should have been blocked + QCOMPARE(entryView->model()->rowCount(), initialEntryCount); + + // Now verify normal deletion still works after modal closes + clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton); + entryView->setFocus(); + QApplication::processEvents(); + + if (!config()->get(Config::Security_NoConfirmMoveEntryToRecycleBin).toBool()) { + MessageBox::setNextAnswer(MessageBox::Move); + } + entryDeleteAction->trigger(); + QApplication::processEvents(); + + // Verify entry was deleted normally + QCOMPARE(entryView->model()->rowCount(), initialEntryCount - 1); +} + void TestGui::addCannedEntries() { // Find buttons diff --git a/tests/gui/TestGui.h b/tests/gui/TestGui.h index 514f7ce95..8a90f07f9 100644 --- a/tests/gui/TestGui.h +++ b/tests/gui/TestGui.h @@ -71,6 +71,7 @@ private slots: void testTrayRestoreHide(); void testShortcutConfig(); void testMenuActionStates(); + void testDeleteEntryDuringModalDialog(); private: void addCannedEntries();