From 5a0da4a9c8d10dc1921077d84bdabf05d20150b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A9ven=20Car?= Date: Mon, 19 Apr 2021 05:10:11 +0000 Subject: [PATCH] When renaming files, move to next file using tab key or up/down To rename previous file: Up or Shift-Tab To rename next file: Down or Tab Credit goes to msciubidlo FEATURE: 403931 FEATURE: 269987 BUG: 334533 FIXED-IN: 21.08 --- src/kitemviews/kitemlistcontainer.cpp | 5 +++ src/kitemviews/kitemlistview.cpp | 7 +++- src/kitemviews/kitemlistview.h | 6 +++ src/kitemviews/kstandarditemlistwidget.cpp | 1 + .../private/kitemlistroleeditor.cpp | 26 ++++++++++++- src/kitemviews/private/kitemlistroleeditor.h | 20 +++++++++- .../private/kitemlistsmoothscroller.cpp | 3 ++ .../private/kitemlistsmoothscroller.h | 5 +++ src/views/dolphinview.cpp | 37 ++++++++++++++----- 9 files changed, 95 insertions(+), 15 deletions(-) diff --git a/src/kitemviews/kitemlistcontainer.cpp b/src/kitemviews/kitemlistcontainer.cpp index f253cda539..10b9f1415c 100644 --- a/src/kitemviews/kitemlistcontainer.cpp +++ b/src/kitemviews/kitemlistcontainer.cpp @@ -208,6 +208,8 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView* disconnect(previous, &KItemListView::maximumItemOffsetChanged, this, &KItemListContainer::updateItemOffsetScrollBar); disconnect(previous, &KItemListView::scrollTo, this, &KItemListContainer::scrollTo); + disconnect(m_horizontalSmoothScroller, &KItemListSmoothScroller::scrollingStopped, previous, &KItemListView::scrollingStopped); + disconnect(m_verticalSmoothScroller, &KItemListSmoothScroller::scrollingStopped, previous, &KItemListView::scrollingStopped); m_horizontalSmoothScroller->setTargetObject(nullptr); m_verticalSmoothScroller->setTargetObject(nullptr); } @@ -224,6 +226,9 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView* connect(current, &KItemListView::maximumItemOffsetChanged, this, &KItemListContainer::updateItemOffsetScrollBar); connect(current, &KItemListView::scrollTo, this, &KItemListContainer::scrollTo); + connect(m_horizontalSmoothScroller, &KItemListSmoothScroller::scrollingStopped, current, &KItemListView::scrollingStopped); + connect(m_verticalSmoothScroller, &KItemListSmoothScroller::scrollingStopped, current, &KItemListView::scrollingStopped); + m_horizontalSmoothScroller->setTargetObject(current); m_verticalSmoothScroller->setTargetObject(current); updateSmoothScrollers(current->scrollOrientation()); diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 14ac22ca97..e4dd7bb2be 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -526,8 +526,11 @@ void KItemListView::scrollToItem(int index) if (newOffset != scrollOffset()) { Q_EMIT scrollTo(newOffset); + return; } } + + Q_EMIT scrollingStopped(); } void KItemListView::beginTransaction() @@ -1602,16 +1605,16 @@ void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, c { disconnectRoleEditingSignals(index); - Q_EMIT roleEditingCanceled(index, role, value); m_editingRole = false; + Q_EMIT roleEditingCanceled(index, role, value); } void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) { disconnectRoleEditingSignals(index); - Q_EMIT roleEditingFinished(index, role, value); m_editingRole = false; + Q_EMIT roleEditingFinished(index, role, value); } void KItemListView::setController(KItemListController* controller) diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h index a847943350..5453d851fd 100644 --- a/src/kitemviews/kitemlistview.h +++ b/src/kitemviews/kitemlistview.h @@ -307,6 +307,12 @@ Q_SIGNALS: void roleEditingCanceled(int index, const QByteArray& role, const QVariant& value); void roleEditingFinished(int index, const QByteArray& role, const QVariant& value); + /** + * Emitted once scrolling has finished, or immediately if no scrolling was necessary + * to get item in view in scrollToItem. + */ + void scrollingStopped(); + protected: QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; void setItemSize(const QSizeF& size); diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 73744b385e..e58340fb89 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -764,6 +764,7 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const m_roleEditor = new KItemListRoleEditor(parent); m_roleEditor->setRole(current); + m_roleEditor->setAllowUpDownKeyChainEdit(m_layout != IconsLayout); m_roleEditor->setFont(styleOption().font); const QString text = data().value(current).toString(); diff --git a/src/kitemviews/private/kitemlistroleeditor.cpp b/src/kitemviews/private/kitemlistroleeditor.cpp index df142a4564..cc10bd58ae 100644 --- a/src/kitemviews/private/kitemlistroleeditor.cpp +++ b/src/kitemviews/private/kitemlistroleeditor.cpp @@ -40,6 +40,11 @@ QByteArray KItemListRoleEditor::role() const return m_role; } +void KItemListRoleEditor::setAllowUpDownKeyChainEdit(bool allowChainEdit) +{ + m_allowUpDownKeyChainEdit = allowChainEdit; +} + bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event) { if (watched == parentWidget() && event->type() == QEvent::Resize) { @@ -78,6 +83,20 @@ void KItemListRoleEditor::keyPressEvent(QKeyEvent* event) emitRoleEditingFinished(); event->accept(); return; + case Qt::Key_Tab: + case Qt::Key_Down: + if (m_allowUpDownKeyChainEdit || event->key() == Qt::Key_Tab) { + emitRoleEditingFinished(EditNext); + event->accept(); + return; + } + case Qt::Key_Backtab: + case Qt::Key_Up: + if (m_allowUpDownKeyChainEdit || event->key() == Qt::Key_Backtab) { + emitRoleEditingFinished(EditPrevious); + event->accept(); + return; + } case Qt::Key_Left: case Qt::Key_Right: { QTextCursor cursor = textCursor(); @@ -143,10 +162,13 @@ void KItemListRoleEditor::autoAdjustSize() } } -void KItemListRoleEditor::emitRoleEditingFinished() +void KItemListRoleEditor::emitRoleEditingFinished(EditResultDirection direction) { + QVariant ret; + ret.setValue(EditResult {KIO::encodeFileName(toPlainText()), direction}); + if (!m_blockFinishedSignal) { - Q_EMIT roleEditingFinished(m_role, KIO::encodeFileName(toPlainText())); + Q_EMIT roleEditingFinished(m_role, ret); } } diff --git a/src/kitemviews/private/kitemlistroleeditor.h b/src/kitemviews/private/kitemlistroleeditor.h index 070cf5ce95..a0f55df51f 100644 --- a/src/kitemviews/private/kitemlistroleeditor.h +++ b/src/kitemviews/private/kitemlistroleeditor.h @@ -11,12 +11,26 @@ #include +enum EditResultDirection{ + EditDone, + EditNext, + EditPrevious, +}; +Q_DECLARE_METATYPE(EditResultDirection) + +struct EditResult +{ + QString newName; + EditResultDirection direction; +}; +Q_DECLARE_METATYPE(EditResult) + /** * @brief Editor for renaming roles of a KItemListWidget. * * Provides signals when the editing got cancelled (e.g. by * pressing Escape or when losing the focus) or when the editing - * got finished (e.g. by pressing Enter or Return). + * got finished (e.g. by pressing Enter, Tab or Return). * * The size automatically gets increased if the text does not fit. */ @@ -31,6 +45,7 @@ public: void setRole(const QByteArray& role); QByteArray role() const; + void setAllowUpDownKeyChainEdit(bool allowChainEdit); bool eventFilter(QObject* watched, QEvent* event) override; Q_SIGNALS: @@ -53,11 +68,12 @@ private: * Emits the signal roleEditingFinished if m_blockFinishedSignal * is false. */ - void emitRoleEditingFinished(); + void emitRoleEditingFinished(EditResultDirection direction = EditDone); private: QByteArray m_role; bool m_blockFinishedSignal; + bool m_allowUpDownKeyChainEdit; }; #endif diff --git a/src/kitemviews/private/kitemlistsmoothscroller.cpp b/src/kitemviews/private/kitemlistsmoothscroller.cpp index 1465361ed3..f77d3df58d 100644 --- a/src/kitemviews/private/kitemlistsmoothscroller.cpp +++ b/src/kitemviews/private/kitemlistsmoothscroller.cpp @@ -175,6 +175,9 @@ void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::Stat if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) { m_smoothScrolling = false; } + if (newState == QAbstractAnimation::Stopped) { + Q_EMIT scrollingStopped(); + } } void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event) diff --git a/src/kitemviews/private/kitemlistsmoothscroller.h b/src/kitemviews/private/kitemlistsmoothscroller.h index 55548219ed..7b2839884f 100644 --- a/src/kitemviews/private/kitemlistsmoothscroller.h +++ b/src/kitemviews/private/kitemlistsmoothscroller.h @@ -69,6 +69,11 @@ public: */ void handleWheelEvent(QWheelEvent* event); +Q_SIGNALS: + /** + * Emitted when the scrolling animation has finished + */ + void scrollingStopped(); protected: bool eventFilter(QObject* obj, QEvent* event) override; diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index f0f67c9d0b..9870664195 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -18,6 +18,7 @@ #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistheader.h" #include "kitemviews/kitemlistselectionmanager.h" +#include "kitemviews/private/kitemlistroleeditor.h" #include "versioncontrol/versioncontrolobserver.h" #include "viewproperties.h" #include "views/tooltips/tooltipmanager.h" @@ -674,12 +675,21 @@ void DolphinView::renameSelectedItems() if (items.count() == 1 && GeneralSettings::renameInline()) { const int index = m_model->index(items.first()); - m_view->editRole(index, "text"); - hideToolTip(); + QMetaObject::Connection * const connection = new QMetaObject::Connection; + *connection = connect(m_view, &KItemListView::scrollingStopped, this, [=](){ + QObject::disconnect(*connection); + delete connection; + + m_view->editRole(index, "text"); + + hideToolTip(); + + connect(m_view, &DolphinItemListView::roleEditingFinished, + this, &DolphinView::slotRoleEditingFinished); + }); + m_view->scrollToItem(index); - connect(m_view, &DolphinItemListView::roleEditingFinished, - this, &DolphinView::slotRoleEditingFinished); } else { KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this); connect(dialog, &KIO::RenameFileDialog::renamingFinished, @@ -1736,13 +1746,15 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con disconnect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); - if (index < 0 || index >= m_model->count()) { + const KFileItemList items = selectedItems(); + if (items.count() != 1) { return; } if (role == "text") { - const KFileItem oldItem = m_model->fileItem(index); - const QString newName = value.toString(); + const KFileItem oldItem = items.first(); + const EditResult retVal = value.value(); + const QString newName = retVal.newName; if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { const QUrl oldUrl = oldItem.url(); @@ -1773,14 +1785,14 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con #endif const bool newNameExistsAlready = (m_model->index(newUrl) >= 0); - if (!newNameExistsAlready) { + if (!newNameExistsAlready && m_model->index(oldUrl) == index) { // Only change the data in the model if no item with the new name // is in the model yet. If there is an item with the new name // already, calling KIO::CopyJob will open a dialog // asking for a new name, and KFileItemModel will update the // data when the dir lister signals that the file name has changed. QHash data; - data.insert(role, value); + data.insert(role, retVal.newName); m_model->setData(index, data); } @@ -1797,6 +1809,13 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con connect(job, &KJob::result, this, &DolphinView::slotRenamingResult); } } + if (retVal.direction != EditDone) { + const short indexShift = retVal.direction == EditNext ? 1 : -1; + m_container->controller()->selectionManager()->setSelected(index, 1, KItemListSelectionManager::Deselect); + m_container->controller()->selectionManager()->setSelected(index + indexShift, 1, + KItemListSelectionManager::Select); + renameSelectedItems(); + } } }