]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Fix inline renaming multiple files when renaming changes sorting order
authorIlia Kats <ilia-kats@gmx.net>
Mon, 9 Dec 2024 11:31:44 +0000 (11:31 +0000)
committerFelix Ernst <felixernst@kde.org>
Mon, 9 Dec 2024 11:31:44 +0000 (11:31 +0000)
Suppose we are renaming file i and the new name will be sorted after
file i+1. We are now pressing ArrowDown to immediately start renaming
file i+1. However, because of the sorting we would not actually end up
renaming what used to be file i+1. What would happen is that editing
would be started in dolphinview.cpp:2065. However, after 100 ms the
timer in KFileItemModel would fire, resulting in the model emitting
itemsMoved(). This would trigger doLayout() in
KItemListView::slotItemsMoved(). doLayout() resizes the
KItemListWidgets, wich causes the renaming to be canceled in
KStandardItemListWidget::resizeEvent().

Now, we start a new renaming operation for the correct widget after the
relayouting is complete.

src/kitemviews/kitemlistview.cpp
src/kitemviews/kitemlistview.h
src/kitemviews/kstandarditemlistwidget.cpp
src/tests/dolphinmainwindowtest.cpp

index d3caa55609ea5c7e5d336982612db9cfa1783353..42c5e25c232fb607fd6d6fa0d2fc68a1ae035971 100644 (file)
@@ -25,6 +25,8 @@
 #include "private/kitemlistsizehintresolver.h"
 #include "private/kitemlistviewlayouter.h"
 
+#include <optional>
+
 #include <QElapsedTimer>
 #include <QGraphicsSceneMouseEvent>
 #include <QGraphicsView>
@@ -743,6 +745,7 @@ void KItemListView::editRole(int index, const QByteArray &role)
     }
 
     m_editingRole = true;
+    m_controller->selectionManager()->setCurrentItem(index);
     widget->setEditedRole(role);
 
     connect(widget, &KItemListWidget::roleEditingCanceled, this, &KItemListView::slotRoleEditingCanceled);
@@ -1376,9 +1379,20 @@ void KItemListView::slotItemsMoved(const KItemRange &itemRange, const QList<int>
     const int firstVisibleMovedIndex = qMax(firstVisibleIndex(), itemRange.index);
     const int lastVisibleMovedIndex = qMin(lastVisibleIndex(), itemRange.index + itemRange.count - 1);
 
+    /// Represents an item that was moved while being edited.
+    struct MovedEditedItem {
+        int movedToIndex;
+        QByteArray editedRole;
+    };
+    std::optional<MovedEditedItem> movedEditedItem;
     for (int index = firstVisibleMovedIndex; index <= lastVisibleMovedIndex; ++index) {
         KItemListWidget *widget = m_visibleItems.value(index);
         if (widget) {
+            if (m_editingRole && !widget->editedRole().isEmpty()) {
+                movedEditedItem = {movedToIndexes[index - itemRange.index], widget->editedRole()};
+                disconnectRoleEditingSignals(index);
+                m_editingRole = false;
+            }
             updateWidgetProperties(widget, index);
             initializeItemListWidget(widget);
         }
@@ -1386,6 +1400,10 @@ void KItemListView::slotItemsMoved(const KItemRange &itemRange, const QList<int>
 
     doLayout(NoAnimation);
     updateSiblingsInformation();
+
+    if (movedEditedItem) {
+        editRole(movedEditedItem->movedToIndex, movedEditedItem->editedRole);
+    }
 }
 
 void KItemListView::slotItemsChanged(const KItemRangeList &itemRanges, const QSet<QByteArray> &roles)
index 7be08302cda128effdec9b9e972af0378aa51943..30ce4d871fc285d82967ed83aedd252178e4fa15 100644 (file)
@@ -794,6 +794,8 @@ private:
     friend class KItemListControllerTest;
     friend class KItemListViewAccessible;
     friend class KItemListDelegateAccessible;
+
+    friend class DolphinMainWindowTest;
 };
 
 /**
index a8fee62449dc28e7c379e5db885b75fde8a5caea..bc7023e126ff0724984c70682ac88da858c13ea8 100644 (file)
@@ -845,16 +845,7 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray &current, const
     if (current.isEmpty() || !parent || current != "text") {
         if (m_roleEditor) {
             Q_EMIT roleEditingCanceled(index(), current, data().value(current));
-
-            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled, this, &KStandardItemListWidget::slotRoleEditingCanceled);
-            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished, this, &KStandardItemListWidget::slotRoleEditingFinished);
-
-            if (m_oldRoleEditor) {
-                m_oldRoleEditor->deleteLater();
-            }
-            m_oldRoleEditor = m_roleEditor;
-            m_roleEditor->hide();
-            m_roleEditor = nullptr;
+            closeRoleEditor();
         }
         return;
     }
index 6ac85e4f04a966c54aa4ba99ca71c4f4fdbc190a..f620cb6bac22b6e509f32fa76ef80b70ca2bb8a5 100644 (file)
@@ -13,7 +13,9 @@
 #include "kitemviews/kitemlistcontainer.h"
 #include "kitemviews/kitemlistcontroller.h"
 #include "kitemviews/kitemlistselectionmanager.h"
+#include "kitemviews/kitemlistwidget.h"
 #include "testdir.h"
+#include "views/dolphinitemlistview.h"
 
 #include <KActionCollection>
 #include <KConfig>
@@ -55,6 +57,7 @@ private Q_SLOTS:
     void testOpenFiles();
     void testAccessibilityTree();
     void testAutoSaveSession();
+    void testInlineRename();
     void cleanupTestCase();
 
 private:
@@ -881,6 +884,59 @@ void DolphinMainWindowTest::testAutoSaveSession()
     m_mainWindow->setSessionAutoSaveEnabled(false);
 }
 
+void DolphinMainWindowTest::testInlineRename()
+{
+    QScopedPointer<TestDir> testDir{new TestDir()};
+    testDir->createFiles({"aaaa", "bbbb", "cccc", "dddd"});
+    m_mainWindow->openDirectories({testDir->url()}, false);
+    m_mainWindow->show();
+    QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data()));
+    QVERIFY(m_mainWindow->isVisible());
+
+    DolphinView *view = m_mainWindow->activeViewContainer()->view();
+    QSignalSpy viewDirectoryLoadingCompletedSpy(view, &DolphinView::directoryLoadingCompleted);
+    QSignalSpy itemsReorderedSpy(view->m_model, &KFileItemModel::itemsMoved);
+    QSignalSpy modelDirectoryLoadingCompletedSpy(view->m_model, &KFileItemModel::directoryLoadingCompleted);
+
+    QVERIFY(viewDirectoryLoadingCompletedSpy.wait());
+    QTest::qWait(500); // we need to wait for the file widgets to become visible
+    view->markUrlsAsSelected({QUrl(testDir->url().toString() + "/aaaa")});
+    view->updateViewState();
+    view->renameSelectedItems();
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Left);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_E);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
+
+    QVERIFY(itemsReorderedSpy.wait());
+    QVERIFY(view->m_view->m_editingRole);
+    KItemListWidget *widget = view->m_view->m_visibleItems.value(view->m_view->firstVisibleIndex());
+    QVERIFY(!widget->editedRole().isEmpty());
+
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Left);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_A);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Left);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_A);
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Down);
+
+    QVERIFY(itemsReorderedSpy.wait());
+    QVERIFY(view->m_view->m_editingRole);
+    widget = view->m_view->m_visibleItems.value(view->m_view->lastVisibleIndex());
+    QVERIFY(!widget->editedRole().isEmpty());
+
+    QTest::keyClick(QApplication::focusWidget(), Qt::Key_Escape);
+    QVERIFY(widget->isCurrent());
+    view->m_model->refreshDirectory(testDir->url());
+    QVERIFY(modelDirectoryLoadingCompletedSpy.wait());
+
+    QCOMPARE(view->m_model->fileItem(0).name(), "abbbb");
+    QCOMPARE(view->m_model->fileItem(1).name(), "adddd");
+    QCOMPARE(view->m_model->fileItem(2).name(), "cccc");
+    QCOMPARE(view->m_model->fileItem(3).name(), "eaaaa");
+    QCOMPARE(view->m_model->count(), 4);
+}
+
 void DolphinMainWindowTest::cleanupTestCase()
 {
     m_mainWindow->showNormal();