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
This commit is contained in:
Méven Car 2021-04-19 05:10:11 +00:00
parent a27b4c5c60
commit 5a0da4a9c8
9 changed files with 95 additions and 15 deletions

View file

@ -208,6 +208,8 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView*
disconnect(previous, &KItemListView::maximumItemOffsetChanged, disconnect(previous, &KItemListView::maximumItemOffsetChanged,
this, &KItemListContainer::updateItemOffsetScrollBar); this, &KItemListContainer::updateItemOffsetScrollBar);
disconnect(previous, &KItemListView::scrollTo, this, &KItemListContainer::scrollTo); 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_horizontalSmoothScroller->setTargetObject(nullptr);
m_verticalSmoothScroller->setTargetObject(nullptr); m_verticalSmoothScroller->setTargetObject(nullptr);
} }
@ -224,6 +226,9 @@ void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView*
connect(current, &KItemListView::maximumItemOffsetChanged, connect(current, &KItemListView::maximumItemOffsetChanged,
this, &KItemListContainer::updateItemOffsetScrollBar); this, &KItemListContainer::updateItemOffsetScrollBar);
connect(current, &KItemListView::scrollTo, this, &KItemListContainer::scrollTo); 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_horizontalSmoothScroller->setTargetObject(current);
m_verticalSmoothScroller->setTargetObject(current); m_verticalSmoothScroller->setTargetObject(current);
updateSmoothScrollers(current->scrollOrientation()); updateSmoothScrollers(current->scrollOrientation());

View file

@ -526,8 +526,11 @@ void KItemListView::scrollToItem(int index)
if (newOffset != scrollOffset()) { if (newOffset != scrollOffset()) {
Q_EMIT scrollTo(newOffset); Q_EMIT scrollTo(newOffset);
return;
} }
} }
Q_EMIT scrollingStopped();
} }
void KItemListView::beginTransaction() void KItemListView::beginTransaction()
@ -1602,16 +1605,16 @@ void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, c
{ {
disconnectRoleEditingSignals(index); disconnectRoleEditingSignals(index);
Q_EMIT roleEditingCanceled(index, role, value);
m_editingRole = false; m_editingRole = false;
Q_EMIT roleEditingCanceled(index, role, value);
} }
void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value)
{ {
disconnectRoleEditingSignals(index); disconnectRoleEditingSignals(index);
Q_EMIT roleEditingFinished(index, role, value);
m_editingRole = false; m_editingRole = false;
Q_EMIT roleEditingFinished(index, role, value);
} }
void KItemListView::setController(KItemListController* controller) void KItemListView::setController(KItemListController* controller)

View file

@ -307,6 +307,12 @@ Q_SIGNALS:
void roleEditingCanceled(int index, const QByteArray& role, const QVariant& value); void roleEditingCanceled(int index, const QByteArray& role, const QVariant& value);
void roleEditingFinished(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: protected:
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
void setItemSize(const QSizeF& size); void setItemSize(const QSizeF& size);

View file

@ -764,6 +764,7 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
m_roleEditor = new KItemListRoleEditor(parent); m_roleEditor = new KItemListRoleEditor(parent);
m_roleEditor->setRole(current); m_roleEditor->setRole(current);
m_roleEditor->setAllowUpDownKeyChainEdit(m_layout != IconsLayout);
m_roleEditor->setFont(styleOption().font); m_roleEditor->setFont(styleOption().font);
const QString text = data().value(current).toString(); const QString text = data().value(current).toString();

View file

@ -40,6 +40,11 @@ QByteArray KItemListRoleEditor::role() const
return m_role; return m_role;
} }
void KItemListRoleEditor::setAllowUpDownKeyChainEdit(bool allowChainEdit)
{
m_allowUpDownKeyChainEdit = allowChainEdit;
}
bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event) bool KItemListRoleEditor::eventFilter(QObject* watched, QEvent* event)
{ {
if (watched == parentWidget() && event->type() == QEvent::Resize) { if (watched == parentWidget() && event->type() == QEvent::Resize) {
@ -78,6 +83,20 @@ void KItemListRoleEditor::keyPressEvent(QKeyEvent* event)
emitRoleEditingFinished(); emitRoleEditingFinished();
event->accept(); event->accept();
return; 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_Left:
case Qt::Key_Right: { case Qt::Key_Right: {
QTextCursor cursor = textCursor(); 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) { if (!m_blockFinishedSignal) {
Q_EMIT roleEditingFinished(m_role, KIO::encodeFileName(toPlainText())); Q_EMIT roleEditingFinished(m_role, ret);
} }
} }

View file

@ -11,12 +11,26 @@
#include <KTextEdit> #include <KTextEdit>
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. * @brief Editor for renaming roles of a KItemListWidget.
* *
* Provides signals when the editing got cancelled (e.g. by * Provides signals when the editing got cancelled (e.g. by
* pressing Escape or when losing the focus) or when the editing * 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. * The size automatically gets increased if the text does not fit.
*/ */
@ -31,6 +45,7 @@ public:
void setRole(const QByteArray& role); void setRole(const QByteArray& role);
QByteArray role() const; QByteArray role() const;
void setAllowUpDownKeyChainEdit(bool allowChainEdit);
bool eventFilter(QObject* watched, QEvent* event) override; bool eventFilter(QObject* watched, QEvent* event) override;
Q_SIGNALS: Q_SIGNALS:
@ -53,11 +68,12 @@ private:
* Emits the signal roleEditingFinished if m_blockFinishedSignal * Emits the signal roleEditingFinished if m_blockFinishedSignal
* is false. * is false.
*/ */
void emitRoleEditingFinished(); void emitRoleEditingFinished(EditResultDirection direction = EditDone);
private: private:
QByteArray m_role; QByteArray m_role;
bool m_blockFinishedSignal; bool m_blockFinishedSignal;
bool m_allowUpDownKeyChainEdit;
}; };
#endif #endif

View file

@ -175,6 +175,9 @@ void KItemListSmoothScroller::slotAnimationStateChanged(QAbstractAnimation::Stat
if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) { if (newState == QAbstractAnimation::Stopped && m_smoothScrolling && !m_scrollBarPressed) {
m_smoothScrolling = false; m_smoothScrolling = false;
} }
if (newState == QAbstractAnimation::Stopped) {
Q_EMIT scrollingStopped();
}
} }
void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event) void KItemListSmoothScroller::handleWheelEvent(QWheelEvent* event)

View file

@ -69,6 +69,11 @@ public:
*/ */
void handleWheelEvent(QWheelEvent* event); void handleWheelEvent(QWheelEvent* event);
Q_SIGNALS:
/**
* Emitted when the scrolling animation has finished
*/
void scrollingStopped();
protected: protected:
bool eventFilter(QObject* obj, QEvent* event) override; bool eventFilter(QObject* obj, QEvent* event) override;

View file

@ -18,6 +18,7 @@
#include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistcontroller.h"
#include "kitemviews/kitemlistheader.h" #include "kitemviews/kitemlistheader.h"
#include "kitemviews/kitemlistselectionmanager.h" #include "kitemviews/kitemlistselectionmanager.h"
#include "kitemviews/private/kitemlistroleeditor.h"
#include "versioncontrol/versioncontrolobserver.h" #include "versioncontrol/versioncontrolobserver.h"
#include "viewproperties.h" #include "viewproperties.h"
#include "views/tooltips/tooltipmanager.h" #include "views/tooltips/tooltipmanager.h"
@ -674,12 +675,21 @@ void DolphinView::renameSelectedItems()
if (items.count() == 1 && GeneralSettings::renameInline()) { if (items.count() == 1 && GeneralSettings::renameInline()) {
const int index = m_model->index(items.first()); 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 { } else {
KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this); KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this);
connect(dialog, &KIO::RenameFileDialog::renamingFinished, connect(dialog, &KIO::RenameFileDialog::renamingFinished,
@ -1736,13 +1746,15 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con
disconnect(m_view, &DolphinItemListView::roleEditingFinished, disconnect(m_view, &DolphinItemListView::roleEditingFinished,
this, &DolphinView::slotRoleEditingFinished); this, &DolphinView::slotRoleEditingFinished);
if (index < 0 || index >= m_model->count()) { const KFileItemList items = selectedItems();
if (items.count() != 1) {
return; return;
} }
if (role == "text") { if (role == "text") {
const KFileItem oldItem = m_model->fileItem(index); const KFileItem oldItem = items.first();
const QString newName = value.toString(); const EditResult retVal = value.value<EditResult>();
const QString newName = retVal.newName;
if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) {
const QUrl oldUrl = oldItem.url(); const QUrl oldUrl = oldItem.url();
@ -1773,14 +1785,14 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, con
#endif #endif
const bool newNameExistsAlready = (m_model->index(newUrl) >= 0); 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 // 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 // is in the model yet. If there is an item with the new name
// already, calling KIO::CopyJob will open a dialog // already, calling KIO::CopyJob will open a dialog
// asking for a new name, and KFileItemModel will update the // asking for a new name, and KFileItemModel will update the
// data when the dir lister signals that the file name has changed. // data when the dir lister signals that the file name has changed.
QHash<QByteArray, QVariant> data; QHash<QByteArray, QVariant> data;
data.insert(role, value); data.insert(role, retVal.newName);
m_model->setData(index, data); 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); 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();
}
} }
} }