mirror of
https://invent.kde.org/system/dolphin
synced 2024-08-22 01:40:02 +00:00
Expand or collapse all selected folders on Key Right/Left
CCBUG: 196772
This commit is contained in:
parent
a1c5c5cf81
commit
c17e6c8d2b
|
@ -237,21 +237,34 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
|
|||
{
|
||||
int index = m_selectionManager->currentItem();
|
||||
int key = event->key();
|
||||
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
|
||||
|
||||
// Handle the expanding/collapsing of items
|
||||
if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) {
|
||||
if (key == Qt::Key_Right) {
|
||||
if (m_model->setExpanded(index, true)) {
|
||||
return true;
|
||||
// expand / collapse all selected directories
|
||||
if (m_view->supportsItemExpanding() && m_model->isExpandable(index) && (key == Qt::Key_Right || key == Qt::Key_Left)) {
|
||||
const bool expandOrCollapse = key == Qt::Key_Right ? true : false;
|
||||
bool shouldReturn = m_model->setExpanded(index, expandOrCollapse);
|
||||
|
||||
// edit in reverse to preserve index of the first handled items
|
||||
const auto selectedItems = m_selectionManager->selectedItems();
|
||||
for (auto it = selectedItems.rbegin(); it != selectedItems.rend(); ++it) {
|
||||
shouldReturn |= m_model->setExpanded(*it, expandOrCollapse);
|
||||
if (!shiftPressed) {
|
||||
m_selectionManager->setSelected(*it);
|
||||
}
|
||||
} else if (key == Qt::Key_Left) {
|
||||
if (m_model->setExpanded(index, false)) {
|
||||
return true;
|
||||
}
|
||||
if (shouldReturn) {
|
||||
// update keyboard anchors
|
||||
if (shiftPressed) {
|
||||
m_keyboardAnchorIndex = selectedItems.count() > 0 ? qMin(index, selectedItems.last()) : index;
|
||||
m_keyboardAnchorPos = keyboardAnchorPos(m_keyboardAnchorIndex);
|
||||
}
|
||||
|
||||
event->ignore();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const bool shiftPressed = event->modifiers() & Qt::ShiftModifier;
|
||||
const bool controlPressed = event->modifiers() & Qt::ControlModifier;
|
||||
const bool shiftOrControlPressed = shiftPressed || controlPressed;
|
||||
const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End || key == Qt::Key_PageUp || key == Qt::Key_PageDown || key == Qt::Key_Up
|
||||
|
@ -327,11 +340,17 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
|
|||
|
||||
case Qt::Key_Up:
|
||||
updateKeyboardAnchor();
|
||||
if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) {
|
||||
m_selectionManager->beginAnchoredSelection(index);
|
||||
}
|
||||
index = previousRowIndex(index);
|
||||
break;
|
||||
|
||||
case Qt::Key_Down:
|
||||
updateKeyboardAnchor();
|
||||
if (shiftPressed && !m_selectionManager->isAnchoredSelectionActive() && m_selectionManager->isSelected(index)) {
|
||||
m_selectionManager->beginAnchoredSelection(index);
|
||||
}
|
||||
index = nextRowIndex(index);
|
||||
break;
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
|
||||
class iterator
|
||||
{
|
||||
iterator(const KItemRangeList::iterator &rangeIt, int offset)
|
||||
iterator(const KItemRangeList::iterator &rangeIt, int offset = 0)
|
||||
: m_rangeIt(rangeIt)
|
||||
, m_offset(offset)
|
||||
{
|
||||
|
@ -135,7 +135,7 @@ public:
|
|||
|
||||
class const_iterator
|
||||
{
|
||||
const_iterator(KItemRangeList::const_iterator rangeIt, int offset)
|
||||
const_iterator(KItemRangeList::const_iterator rangeIt, int offset = 0)
|
||||
: m_rangeIt(rangeIt)
|
||||
, m_offset(offset)
|
||||
{
|
||||
|
@ -223,6 +223,70 @@ public:
|
|||
friend class KItemSet;
|
||||
};
|
||||
|
||||
class const_reverse_iterator
|
||||
{
|
||||
public:
|
||||
const_reverse_iterator(KItemSet::const_iterator rangeIt)
|
||||
: m_current(rangeIt)
|
||||
{
|
||||
}
|
||||
|
||||
const_reverse_iterator(const KItemSet::const_reverse_iterator &other)
|
||||
: m_current(other.base())
|
||||
{
|
||||
}
|
||||
|
||||
int operator*() const
|
||||
{
|
||||
// analog to std::prev
|
||||
auto t = const_iterator(m_current);
|
||||
--t;
|
||||
return *t;
|
||||
}
|
||||
|
||||
inline bool operator==(const const_reverse_iterator &other) const
|
||||
{
|
||||
return m_current == other.m_current;
|
||||
}
|
||||
|
||||
bool operator!=(const const_reverse_iterator &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
const_reverse_iterator &operator++()
|
||||
{
|
||||
--m_current;
|
||||
return *this;
|
||||
}
|
||||
const_reverse_iterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
const_reverse_iterator &operator--()
|
||||
{
|
||||
++m_current;
|
||||
return *this;
|
||||
}
|
||||
const_reverse_iterator operator--(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
KItemSet::const_iterator base() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
private:
|
||||
KItemSet::const_iterator m_current;
|
||||
};
|
||||
|
||||
iterator begin();
|
||||
const_iterator begin() const;
|
||||
const_iterator constBegin() const;
|
||||
|
@ -230,6 +294,9 @@ public:
|
|||
const_iterator end() const;
|
||||
const_iterator constEnd() const;
|
||||
|
||||
const_reverse_iterator rend() const;
|
||||
const_reverse_iterator rbegin() const;
|
||||
|
||||
int first() const;
|
||||
int last() const;
|
||||
|
||||
|
@ -366,32 +433,32 @@ inline bool KItemSet::remove(int i)
|
|||
|
||||
inline KItemSet::iterator KItemSet::begin()
|
||||
{
|
||||
return iterator(m_itemRanges.begin(), 0);
|
||||
return iterator(m_itemRanges.begin());
|
||||
}
|
||||
|
||||
inline KItemSet::const_iterator KItemSet::begin() const
|
||||
{
|
||||
return const_iterator(m_itemRanges.begin(), 0);
|
||||
return const_iterator(m_itemRanges.begin());
|
||||
}
|
||||
|
||||
inline KItemSet::const_iterator KItemSet::constBegin() const
|
||||
{
|
||||
return const_iterator(m_itemRanges.constBegin(), 0);
|
||||
return const_iterator(m_itemRanges.constBegin());
|
||||
}
|
||||
|
||||
inline KItemSet::iterator KItemSet::end()
|
||||
{
|
||||
return iterator(m_itemRanges.end(), 0);
|
||||
return iterator(m_itemRanges.end());
|
||||
}
|
||||
|
||||
inline KItemSet::const_iterator KItemSet::end() const
|
||||
{
|
||||
return const_iterator(m_itemRanges.end(), 0);
|
||||
return const_iterator(m_itemRanges.end());
|
||||
}
|
||||
|
||||
inline KItemSet::const_iterator KItemSet::constEnd() const
|
||||
{
|
||||
return const_iterator(m_itemRanges.constEnd(), 0);
|
||||
return const_iterator(m_itemRanges.constEnd());
|
||||
}
|
||||
|
||||
inline int KItemSet::first() const
|
||||
|
@ -405,6 +472,16 @@ inline int KItemSet::last() const
|
|||
return lastRange.index + lastRange.count - 1;
|
||||
}
|
||||
|
||||
inline KItemSet::const_reverse_iterator KItemSet::rend() const
|
||||
{
|
||||
return KItemSet::const_reverse_iterator(constBegin());
|
||||
}
|
||||
|
||||
inline KItemSet::const_reverse_iterator KItemSet::rbegin() const
|
||||
{
|
||||
return KItemSet::const_reverse_iterator(constEnd());
|
||||
}
|
||||
|
||||
inline KItemSet &KItemSet::operator<<(int i)
|
||||
{
|
||||
insert(i);
|
||||
|
|
|
@ -20,6 +20,11 @@ ecm_add_test(kitemlistcontrollertest.cpp testdir.cpp
|
|||
TEST_NAME kitemlistcontrollertest
|
||||
LINK_LIBRARIES dolphinprivate Qt${QT_MAJOR_VERSION}::Test)
|
||||
|
||||
# KItemListControllerExpandTest
|
||||
ecm_add_test(kitemlistcontrollerexpandtest.cpp testdir.cpp
|
||||
TEST_NAME kitemlistcontrollerexpandtest
|
||||
LINK_LIBRARIES dolphinprivate Qt${QT_MAJOR_VERSION}::Test)
|
||||
|
||||
# KFileItemListViewTest
|
||||
ecm_add_test(kfileitemlistviewtest.cpp testdir.cpp
|
||||
TEST_NAME kfileitemlistviewtest
|
||||
|
|
235
src/tests/kitemlistcontrollerexpandtest.cpp
Normal file
235
src/tests/kitemlistcontrollerexpandtest.cpp
Normal file
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Méven Car <meven@kde.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kitemviews/kfileitemlistview.h"
|
||||
#include "kitemviews/kfileitemmodel.h"
|
||||
#include "kitemviews/kitemlistcontainer.h"
|
||||
#include "kitemviews/kitemlistcontroller.h"
|
||||
#include "kitemviews/kitemlistselectionmanager.h"
|
||||
#include "testdir.h"
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QStandardPaths>
|
||||
#include <QTest>
|
||||
|
||||
class KItemListControllerExpandTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testDirExpand();
|
||||
|
||||
private:
|
||||
KFileItemListView *m_view;
|
||||
KItemListController *m_controller;
|
||||
KItemListSelectionManager *m_selectionManager;
|
||||
KFileItemModel *m_model;
|
||||
TestDir *m_testDir;
|
||||
KItemListContainer *m_container;
|
||||
QSignalSpy *m_spyDirectoryLoadingCompleted;
|
||||
};
|
||||
|
||||
void KItemListControllerExpandTest::initTestCase()
|
||||
{
|
||||
QStandardPaths::setTestModeEnabled(true);
|
||||
qRegisterMetaType<KItemSet>("KItemSet");
|
||||
|
||||
m_testDir = new TestDir();
|
||||
m_model = new KFileItemModel();
|
||||
m_view = new KFileItemListView();
|
||||
m_controller = new KItemListController(m_model, m_view, this);
|
||||
m_container = new KItemListContainer(m_controller);
|
||||
m_controller = m_container->controller();
|
||||
m_controller->setSelectionBehavior(KItemListController::MultiSelection);
|
||||
m_selectionManager = m_controller->selectionManager();
|
||||
|
||||
QStringList files;
|
||||
files << "dir1/file1";
|
||||
files << "dir1/file2";
|
||||
files << "dir1/file3";
|
||||
files << "dir1/file4";
|
||||
files << "dir1/file5";
|
||||
|
||||
files << "dir2/file1";
|
||||
files << "dir2/file2";
|
||||
files << "dir2/file3";
|
||||
files << "dir2/file4";
|
||||
files << "dir2/file5";
|
||||
|
||||
files << "dir3/file1";
|
||||
files << "dir3/file2";
|
||||
files << "dir3/file3";
|
||||
files << "dir3/file4";
|
||||
files << "dir3/file5";
|
||||
|
||||
m_testDir->createFiles(files);
|
||||
m_model->loadDirectory(m_testDir->url());
|
||||
m_spyDirectoryLoadingCompleted = new QSignalSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
|
||||
QVERIFY(m_spyDirectoryLoadingCompleted->wait());
|
||||
|
||||
m_container->show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(m_container));
|
||||
}
|
||||
void KItemListControllerExpandTest::cleanupTestCase()
|
||||
{
|
||||
delete m_container;
|
||||
m_container = nullptr;
|
||||
|
||||
delete m_testDir;
|
||||
m_testDir = nullptr;
|
||||
}
|
||||
|
||||
void KItemListControllerExpandTest::init()
|
||||
{
|
||||
m_selectionManager->setCurrentItem(0);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
|
||||
m_selectionManager->clearSelection();
|
||||
QVERIFY(!m_selectionManager->hasSelection());
|
||||
}
|
||||
|
||||
void KItemListControllerExpandTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void KItemListControllerExpandTest::testDirExpand()
|
||||
{
|
||||
m_view->setItemLayout(KFileItemListView::DetailsLayout);
|
||||
QCOMPARE(m_view->itemLayout(), KFileItemListView::DetailsLayout);
|
||||
m_view->setSupportsItemExpanding(true);
|
||||
|
||||
// intial state
|
||||
QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 1);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 0);
|
||||
|
||||
// extend first folder
|
||||
QTest::keyClick(m_container, Qt::Key_Right);
|
||||
QVERIFY(m_spyDirectoryLoadingCompleted->wait());
|
||||
QCOMPARE(m_model->count(), 8);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 0);
|
||||
|
||||
// collapse folder
|
||||
QTest::keyClick(m_container, Qt::Key_Left);
|
||||
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 0);
|
||||
|
||||
// make the first folder selected
|
||||
QTest::keyClick(m_container, Qt::Key_Down);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 1);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 1);
|
||||
|
||||
QTest::keyClick(m_container, Qt::Key_Up);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 1);
|
||||
|
||||
// expand the two first folders
|
||||
QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 1);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 2);
|
||||
|
||||
// precondition
|
||||
QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 2);
|
||||
|
||||
// expand selected folders
|
||||
QTest::keyClick(m_container, Qt::Key_Right);
|
||||
QVERIFY(QTest::qWaitFor(
|
||||
[this]() {
|
||||
return m_spyDirectoryLoadingCompleted->count() == 3;
|
||||
},
|
||||
100));
|
||||
QCOMPARE(m_model->count(), 8);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 6);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 2);
|
||||
|
||||
// collapse the folders
|
||||
QTest::keyClick(m_container, Qt::Key_Left);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 1);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 2);
|
||||
|
||||
// select third folder
|
||||
QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 2);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 3);
|
||||
|
||||
// precondition
|
||||
QCOMPARE(m_spyDirectoryLoadingCompleted->count(), 3);
|
||||
|
||||
// expand the three folders
|
||||
QTest::keyClick(m_container, Qt::Key_Right);
|
||||
|
||||
QVERIFY(QTest::qWaitFor(
|
||||
[this]() {
|
||||
return m_spyDirectoryLoadingCompleted->count() == 6;
|
||||
},
|
||||
100));
|
||||
|
||||
QCOMPARE(m_model->count(), 18);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 12);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 3);
|
||||
|
||||
// collapse the folders
|
||||
QTest::keyClick(m_container, Qt::Key_Left);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 2);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 3);
|
||||
|
||||
// shift select the directories
|
||||
QTest::keyClick(m_container, Qt::Key_Up);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 1);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 1);
|
||||
|
||||
QTest::keyClick(m_container, Qt::Key_Up);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 0);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 1);
|
||||
|
||||
QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier);
|
||||
QTest::keyClick(m_container, Qt::Key_Down, Qt::ShiftModifier);
|
||||
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 2);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 3);
|
||||
|
||||
// expand the three folders with shift modifier
|
||||
QTest::keyClick(m_container, Qt::Key_Right, Qt::ShiftModifier);
|
||||
|
||||
QVERIFY(QTest::qWaitFor(
|
||||
[this]() {
|
||||
return m_spyDirectoryLoadingCompleted->count() == 9;
|
||||
},
|
||||
100));
|
||||
|
||||
QCOMPARE(m_model->count(), 18);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 12);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 13);
|
||||
|
||||
// collapse the folders
|
||||
QTest::keyClick(m_container, Qt::Key_Left);
|
||||
QCOMPARE(m_model->count(), 3);
|
||||
QCOMPARE(m_selectionManager->currentItem(), 2);
|
||||
QCOMPARE(m_selectionManager->selectedItems().count(), 3);
|
||||
}
|
||||
|
||||
QTEST_MAIN(KItemListControllerExpandTest)
|
||||
|
||||
#include "kitemlistcontrollerexpandtest.moc"
|
Loading…
Reference in a new issue