File indexing completed on 2024-04-28 05:45:21
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Frank Reininghaus <frank78ac@googlemail.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kitemviews/kitemlistcontroller.h" 0008 #include "kitemviews/kfileitemlistview.h" 0009 #include "kitemviews/kfileitemmodel.h" 0010 #include "kitemviews/kitemlistcontainer.h" 0011 #include "kitemviews/kitemlistselectionmanager.h" 0012 #include "kitemviews/private/kitemlistviewlayouter.h" 0013 #include "testdir.h" 0014 0015 #include <QGraphicsSceneMouseEvent> 0016 #include <QProxyStyle> 0017 #include <QSignalSpy> 0018 #include <QStandardPaths> 0019 #include <QTest> 0020 0021 /** 0022 * \class KItemListControllerTestStyle is a proxy style for testing the 0023 * KItemListController with different style hint options, e.g. single/double 0024 * click activation. 0025 */ 0026 class KItemListControllerTestStyle : public QProxyStyle 0027 { 0028 Q_OBJECT 0029 public: 0030 KItemListControllerTestStyle(QStyle *style) 0031 : QProxyStyle(style) 0032 , m_activateItemOnSingleClick((bool)style->styleHint(SH_ItemView_ActivateItemOnSingleClick)) 0033 { 0034 } 0035 0036 void setActivateItemOnSingleClick(bool activateItemOnSingleClick) 0037 { 0038 m_activateItemOnSingleClick = activateItemOnSingleClick; 0039 } 0040 0041 bool activateItemOnSingleClick() const 0042 { 0043 return m_activateItemOnSingleClick; 0044 } 0045 0046 int styleHint(StyleHint hint, const QStyleOption *option = nullptr, const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override 0047 { 0048 switch (hint) { 0049 case QStyle::SH_ItemView_ActivateItemOnSingleClick: 0050 return (int)activateItemOnSingleClick(); 0051 default: 0052 return QProxyStyle::styleHint(hint, option, widget, returnData); 0053 } 0054 } 0055 0056 private: 0057 bool m_activateItemOnSingleClick; 0058 }; 0059 0060 Q_DECLARE_METATYPE(KFileItemListView::ItemLayout) 0061 Q_DECLARE_METATYPE(Qt::Orientation) 0062 Q_DECLARE_METATYPE(KItemListController::SelectionBehavior) 0063 Q_DECLARE_METATYPE(KItemSet) 0064 0065 class KItemListControllerTest : public QObject 0066 { 0067 Q_OBJECT 0068 0069 private Q_SLOTS: 0070 void initTestCase(); 0071 void cleanupTestCase(); 0072 0073 void init(); 0074 void cleanup(); 0075 0076 void testKeyboardNavigation_data(); 0077 void testKeyboardNavigation(); 0078 void testMouseClickActivation(); 0079 0080 private: 0081 /** 0082 * Make sure that the number of columns in the view is equal to \a count 0083 * by changing the geometry of the container. 0084 */ 0085 void adjustGeometryForColumnCount(int count); 0086 0087 private: 0088 KFileItemListView *m_view; 0089 KItemListController *m_controller; 0090 KItemListSelectionManager *m_selectionManager; 0091 KFileItemModel *m_model; 0092 TestDir *m_testDir; 0093 KItemListContainer *m_container; 0094 KItemListControllerTestStyle *m_testStyle; 0095 }; 0096 0097 /** 0098 * This function initializes the member objects, creates the temporary files, and 0099 * shows the view on the screen on startup. This could also be done before every 0100 * single test, but this would make the time needed to run the test much larger. 0101 */ 0102 void KItemListControllerTest::initTestCase() 0103 { 0104 QStandardPaths::setTestModeEnabled(true); 0105 qRegisterMetaType<KItemSet>("KItemSet"); 0106 0107 m_testDir = new TestDir(); 0108 m_model = new KFileItemModel(); 0109 m_view = new KFileItemListView(); 0110 m_controller = new KItemListController(m_model, m_view, this); 0111 m_container = new KItemListContainer(m_controller); 0112 #ifndef QT_NO_ACCESSIBILITY 0113 m_view->setAccessibleParentsObject(m_container); 0114 #endif 0115 m_controller = m_container->controller(); 0116 m_controller->setSelectionBehavior(KItemListController::MultiSelection); 0117 m_selectionManager = m_controller->selectionManager(); 0118 m_testStyle = new KItemListControllerTestStyle(m_view->style()); 0119 m_view->setStyle(m_testStyle); 0120 0121 QStringList files; 0122 files << "a1" 0123 << "a2" 0124 << "a3" 0125 << "b1" 0126 << "c1" 0127 << "c2" 0128 << "c3" 0129 << "c4" 0130 << "c5" 0131 << "d1" 0132 << "d2" 0133 << "d3" 0134 << "d4" 0135 << "e" 0136 << "e 2" 0137 << "e 3" 0138 << "e 4" 0139 << "e 5" 0140 << "e 6" 0141 << "e 7"; 0142 0143 m_testDir->createFiles(files); 0144 m_model->loadDirectory(m_testDir->url()); 0145 QSignalSpy spyDirectoryLoadingCompleted(m_model, &KFileItemModel::directoryLoadingCompleted); 0146 QVERIFY(spyDirectoryLoadingCompleted.wait()); 0147 0148 m_container->show(); 0149 QVERIFY(QTest::qWaitForWindowExposed(m_container)); 0150 } 0151 0152 void KItemListControllerTest::cleanupTestCase() 0153 { 0154 delete m_container; 0155 m_container = nullptr; 0156 0157 delete m_testDir; 0158 m_testDir = nullptr; 0159 } 0160 0161 /** Before each test, the current item, selection, and item size are reset to the defaults. */ 0162 void KItemListControllerTest::init() 0163 { 0164 m_selectionManager->setCurrentItem(0); 0165 QCOMPARE(m_selectionManager->currentItem(), 0); 0166 0167 m_selectionManager->clearSelection(); 0168 QVERIFY(!m_selectionManager->hasSelection()); 0169 0170 const QSizeF itemSize(50, 50); 0171 m_view->setItemSize(itemSize); 0172 QCOMPARE(m_view->itemSize(), itemSize); 0173 } 0174 0175 void KItemListControllerTest::cleanup() 0176 { 0177 } 0178 0179 /** 0180 * \class KeyPress is a small helper struct that represents a key press event, 0181 * including the key and the keyboard modifiers. 0182 */ 0183 struct KeyPress { 0184 KeyPress(Qt::Key key, Qt::KeyboardModifiers modifier = Qt::NoModifier) 0185 : m_key(key) 0186 , m_modifier(modifier) 0187 { 0188 } 0189 0190 Qt::Key m_key; 0191 Qt::KeyboardModifiers m_modifier; 0192 }; 0193 0194 /** 0195 * \class ViewState is a small helper struct that represents a certain state 0196 * of the view, including the current item, the selected items in MultiSelection 0197 * mode (in the other modes, the selection is either empty or equal to the 0198 * current item), and the information whether items were activated by the last 0199 * key press. 0200 */ 0201 struct ViewState { 0202 ViewState(int current, const KItemSet &selection, bool activated = false) 0203 : m_current(current) 0204 , m_selection(selection) 0205 , m_activated(activated) 0206 { 0207 } 0208 0209 int m_current; 0210 KItemSet m_selection; 0211 bool m_activated; 0212 }; 0213 0214 // We have to define a typedef for the pair in order to make the test compile. 0215 typedef QPair<KeyPress, ViewState> keyPressViewStatePair; 0216 Q_DECLARE_METATYPE(QList<keyPressViewStatePair>) 0217 0218 /** 0219 * This function provides the data for the actual test function 0220 * KItemListControllerTest::testKeyboardNavigation(). 0221 * It tests all possible combinations of view layouts, selection behaviors, 0222 * and enabled/disabled groupings for different column counts, and 0223 * provides a list of key presses and the states that the view should be in 0224 * after the key press event. 0225 */ 0226 void KItemListControllerTest::testKeyboardNavigation_data() 0227 { 0228 QTest::addColumn<KFileItemListView::ItemLayout>("layout"); 0229 QTest::addColumn<Qt::Orientation>("scrollOrientation"); 0230 QTest::addColumn<int>("columnCount"); 0231 QTest::addColumn<KItemListController::SelectionBehavior>("selectionBehavior"); 0232 QTest::addColumn<bool>("groupingEnabled"); 0233 QTest::addColumn<Qt::LayoutDirection>("layoutDirection"); 0234 QTest::addColumn<QList<QPair<KeyPress, ViewState>>>("testList"); 0235 0236 QList<KFileItemListView::ItemLayout> layoutList; 0237 QHash<KFileItemListView::ItemLayout, QString> layoutNames; 0238 layoutList.append(KFileItemListView::IconsLayout); 0239 layoutNames[KFileItemListView::IconsLayout] = "Icons"; 0240 layoutList.append(KFileItemListView::CompactLayout); 0241 layoutNames[KFileItemListView::CompactLayout] = "Compact"; 0242 layoutList.append(KFileItemListView::DetailsLayout); 0243 layoutNames[KFileItemListView::DetailsLayout] = "Details"; 0244 0245 QList<KItemListController::SelectionBehavior> selectionBehaviorList; 0246 QHash<KItemListController::SelectionBehavior, QString> selectionBehaviorNames; 0247 selectionBehaviorList.append(KItemListController::NoSelection); 0248 selectionBehaviorNames[KItemListController::NoSelection] = "NoSelection"; 0249 selectionBehaviorList.append(KItemListController::SingleSelection); 0250 selectionBehaviorNames[KItemListController::SingleSelection] = "SingleSelection"; 0251 selectionBehaviorList.append(KItemListController::MultiSelection); 0252 selectionBehaviorNames[KItemListController::MultiSelection] = "MultiSelection"; 0253 0254 QList<bool> groupingEnabledList; 0255 QHash<bool, QString> groupingEnabledNames; 0256 groupingEnabledList.append(false); 0257 groupingEnabledNames[false] = "ungrouped"; 0258 groupingEnabledList.append(true); 0259 groupingEnabledNames[true] = "grouping enabled"; 0260 0261 QList<Qt::LayoutDirection> layoutDirectionList; 0262 QHash<Qt::LayoutDirection, QString> layoutDirectionNames; 0263 layoutDirectionList.append(Qt::LeftToRight); 0264 layoutDirectionNames[Qt::LeftToRight] = "Left-to-Right LayoutDirection"; 0265 layoutDirectionList.append(Qt::RightToLeft); 0266 layoutDirectionNames[Qt::RightToLeft] = "Right-to-Left LayoutDirection"; 0267 0268 for (const KFileItemListView::ItemLayout &layout : layoutList) { 0269 // The following settings depend on the layout. 0270 // Note that 'columns' are actually 'rows' in 0271 // Compact layout. 0272 Qt::Orientation scrollOrientation; 0273 QList<int> columnCountList; 0274 Qt::Key nextItemKey; 0275 Qt::Key previousItemKey; 0276 Qt::Key nextRowKey; 0277 Qt::Key previousRowKey; 0278 0279 switch (layout) { 0280 case KFileItemListView::IconsLayout: 0281 scrollOrientation = Qt::Vertical; 0282 columnCountList << 1 << 3 << 5; 0283 nextItemKey = Qt::Key_Right; 0284 previousItemKey = Qt::Key_Left; 0285 nextRowKey = Qt::Key_Down; 0286 previousRowKey = Qt::Key_Up; 0287 break; 0288 case KFileItemListView::CompactLayout: 0289 scrollOrientation = Qt::Horizontal; 0290 columnCountList << 1 << 3 << 5; 0291 nextItemKey = Qt::Key_Down; 0292 previousItemKey = Qt::Key_Up; 0293 nextRowKey = Qt::Key_Right; 0294 previousRowKey = Qt::Key_Left; 0295 break; 0296 case KFileItemListView::DetailsLayout: 0297 scrollOrientation = Qt::Vertical; 0298 columnCountList << 1; 0299 nextItemKey = Qt::Key_Down; 0300 previousItemKey = Qt::Key_Up; 0301 nextRowKey = Qt::Key_Down; 0302 previousRowKey = Qt::Key_Up; 0303 break; 0304 } 0305 for (auto layoutDirection : std::as_const(layoutDirectionList)) { 0306 if (layoutDirection == Qt::RightToLeft) { 0307 switch (layout) { 0308 case KFileItemListView::IconsLayout: 0309 std::swap(nextItemKey, previousItemKey); 0310 break; 0311 case KFileItemListView::CompactLayout: 0312 std::swap(nextItemKey, previousItemKey); 0313 break; 0314 default: 0315 break; 0316 } 0317 } 0318 for (int columnCount : std::as_const(columnCountList)) { 0319 for (const KItemListController::SelectionBehavior &selectionBehavior : std::as_const(selectionBehaviorList)) { 0320 for (bool groupingEnabled : std::as_const(groupingEnabledList)) { 0321 QList<QPair<KeyPress, ViewState>> testList; 0322 0323 // First, key presses which should have the same effect 0324 // for any layout and any number of columns. 0325 testList << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) 0326 << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, KItemSet() << 1, true)) 0327 << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, KItemSet() << 1, true)) 0328 << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2)) 0329 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) 0330 << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, KItemSet() << 2 << 3, true)) 0331 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 2)) 0332 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) 0333 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 2 << 3)) 0334 << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, KItemSet() << 2 << 3, true)) 0335 << qMakePair(KeyPress(previousItemKey), ViewState(3, KItemSet() << 3)) 0336 << qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, KItemSet() << 0 << 1 << 2 << 3)) 0337 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3)) 0338 << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 2 << 3)) 0339 << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3)) 0340 << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19)) 0341 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, KItemSet() << 18 << 19)) 0342 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)) 0343 << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet())) 0344 << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, KItemSet(), true)) 0345 << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 0)) 0346 << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet())) 0347 << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet() << 0)) 0348 << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13)) 0349 << qMakePair(KeyPress(Qt::Key_Space), ViewState(14, KItemSet() << 14)) 0350 << qMakePair(KeyPress(Qt::Key_3), ViewState(15, KItemSet() << 15)) 0351 << qMakePair(KeyPress(Qt::Key_Escape), ViewState(15, KItemSet())) 0352 << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13)) 0353 << qMakePair(KeyPress(Qt::Key_E), ViewState(14, KItemSet() << 14)) 0354 << qMakePair(KeyPress(Qt::Key_E), ViewState(15, KItemSet() << 15)) 0355 << qMakePair(KeyPress(Qt::Key_Escape), ViewState(15, KItemSet())) 0356 << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13)) 0357 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)) 0358 << qMakePair(KeyPress(Qt::Key_Escape), ViewState(0, KItemSet())); 0359 0360 // Next, we test combinations of key presses which only work for a 0361 // particular number of columns and either enabled or disabled grouping. 0362 0363 // One column. 0364 if (columnCount == 1) { 0365 testList << qMakePair(KeyPress(nextRowKey), ViewState(1, KItemSet() << 1)) 0366 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 1 << 2)) 0367 << qMakePair(KeyPress(nextRowKey, Qt::ControlModifier), ViewState(3, KItemSet() << 1 << 2)) 0368 << qMakePair(KeyPress(previousRowKey), ViewState(2, KItemSet() << 2)) 0369 << qMakePair(KeyPress(previousItemKey), ViewState(1, KItemSet() << 1)) 0370 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); 0371 } 0372 0373 // Multiple columns: we test both 3 and 5 columns with grouping 0374 // enabled or disabled. For each case, the layout of the items 0375 // in the view is shown (both using file names and indices) to 0376 // make it easier to understand what the tests do. 0377 0378 if (columnCount == 3 && !groupingEnabled) { 0379 // 3 columns, no grouping: 0380 // 0381 // a1 a2 a3 | 0 1 2 0382 // b1 c1 c2 | 3 4 5 0383 // c3 c4 c5 | 6 7 8 0384 // d1 d2 d3 | 9 10 11 0385 // d4 e1 e2 | 12 13 14 0386 // e3 e4 e5 | 15 16 17 0387 // e6 e7 | 18 19 0388 testList << qMakePair(KeyPress(nextRowKey), ViewState(3, KItemSet() << 3)) 0389 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 3)) 0390 << qMakePair(KeyPress(nextRowKey), ViewState(7, KItemSet() << 7)) 0391 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8)) 0392 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(9, KItemSet() << 7 << 8 << 9)) 0393 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 7 << 8)) 0394 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7)) 0395 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7)) 0396 << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 5 << 6 << 7)) 0397 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 6 << 7)) 0398 << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7)) 0399 << qMakePair(KeyPress(nextRowKey), ViewState(10, KItemSet() << 10)) 0400 << qMakePair(KeyPress(nextItemKey), ViewState(11, KItemSet() << 11)) 0401 << qMakePair(KeyPress(nextRowKey), ViewState(14, KItemSet() << 14)) 0402 << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17)) 0403 << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) 0404 << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) 0405 << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 19)) 0406 << qMakePair(KeyPress(previousRowKey), ViewState(16, KItemSet() << 16)) 0407 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); 0408 } 0409 0410 if (columnCount == 5 && !groupingEnabled) { 0411 // 5 columns, no grouping: 0412 // 0413 // a1 a2 a3 b1 c1 | 0 1 2 3 4 0414 // c2 c3 c4 c5 d1 | 5 6 7 8 9 0415 // d2 d3 d4 e1 e2 | 10 11 12 13 14 0416 // e3 e4 e5 e6 e7 | 15 16 17 18 19 0417 testList << qMakePair(KeyPress(nextRowKey), ViewState(5, KItemSet() << 5)) 0418 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(6, KItemSet() << 5)) 0419 << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11)) 0420 << qMakePair(KeyPress(nextItemKey), ViewState(12, KItemSet() << 12)) 0421 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(17, KItemSet() << 12 << 13 << 14 << 15 << 16 << 17)) 0422 << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12)) 0423 << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 7 << 8 << 9 << 10 << 11 << 12)) 0424 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(12, KItemSet() << 12)) 0425 << qMakePair(KeyPress(Qt::Key_End, Qt::ControlModifier), ViewState(19, KItemSet() << 12)) 0426 << qMakePair(KeyPress(previousRowKey), ViewState(14, KItemSet() << 14)) 0427 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); 0428 } 0429 0430 if (columnCount == 3 && groupingEnabled) { 0431 // 3 columns, with grouping: 0432 // 0433 // a1 a2 a3 | 0 1 2 0434 // b1 | 3 0435 // c1 c2 c3 | 4 5 6 0436 // c4 c5 | 7 8 0437 // d1 d2 d3 | 9 10 11 0438 // d4 | 12 0439 // e1 e2 e3 | 13 14 15 0440 // e4 e5 e6 | 16 17 18 0441 // e7 | 19 0442 testList << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) 0443 << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet() << 2)) 0444 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) 0445 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 2 << 3 << 4 << 5 << 6)) 0446 << qMakePair(KeyPress(nextRowKey), ViewState(8, KItemSet() << 8)) 0447 << qMakePair(KeyPress(nextRowKey), ViewState(11, KItemSet() << 11)) 0448 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(12, KItemSet() << 11)) 0449 << qMakePair(KeyPress(nextRowKey), ViewState(13, KItemSet() << 13)) 0450 << qMakePair(KeyPress(nextRowKey), ViewState(16, KItemSet() << 16)) 0451 << qMakePair(KeyPress(nextItemKey), ViewState(17, KItemSet() << 17)) 0452 << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) 0453 << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) 0454 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); 0455 } 0456 0457 if (columnCount == 5 && groupingEnabled) { 0458 // 5 columns, with grouping: 0459 // 0460 // a1 a2 a3 | 0 1 2 0461 // b1 | 3 0462 // c1 c2 c3 c4 c5 | 4 5 6 7 8 0463 // d1 d2 d3 d4 | 9 10 11 12 0464 // e1 e2 e3 e4 e5 | 13 14 15 16 17 0465 // e6 e7 | 18 19 0466 testList << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet() << 1)) 0467 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 1 << 2 << 3)) 0468 << qMakePair(KeyPress(nextRowKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 1 << 2 << 3 << 4 << 5)) 0469 << qMakePair(KeyPress(nextItemKey), ViewState(6, KItemSet() << 6)) 0470 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(7, KItemSet() << 6)) 0471 << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(8, KItemSet() << 6)) 0472 << qMakePair(KeyPress(nextRowKey), ViewState(12, KItemSet() << 12)) 0473 << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 17)) 0474 << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 19)) 0475 << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 17)) 0476 << qMakePair(KeyPress(Qt::Key_End, Qt::ShiftModifier), ViewState(19, KItemSet() << 17 << 18 << 19)) 0477 << qMakePair(KeyPress(previousRowKey, Qt::ShiftModifier), ViewState(14, KItemSet() << 14 << 15 << 16 << 17)) 0478 << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0)); 0479 } 0480 0481 const QString testName = layoutNames[layout] + ", " + QString("%1 columns, ").arg(columnCount) 0482 + selectionBehaviorNames[selectionBehavior] + ", " + groupingEnabledNames[groupingEnabled] + ", " 0483 + layoutDirectionNames[layoutDirection]; 0484 0485 const QByteArray testNameAscii = testName.toLatin1(); 0486 0487 QTest::newRow(testNameAscii.data()) 0488 << layout << scrollOrientation << columnCount << selectionBehavior << groupingEnabled << layoutDirection << testList; 0489 } 0490 } 0491 } 0492 } 0493 } 0494 } 0495 0496 /** 0497 * This function sets the view's properties according to the data provided by 0498 * KItemListControllerTest::testKeyboardNavigation_data(). 0499 * 0500 * The list \a testList contains pairs of key presses, which are sent to the 0501 * container, and expected view states, which are verified then. 0502 */ 0503 void KItemListControllerTest::testKeyboardNavigation() 0504 { 0505 QFETCH(KFileItemListView::ItemLayout, layout); 0506 QFETCH(Qt::Orientation, scrollOrientation); 0507 QFETCH(int, columnCount); 0508 QFETCH(KItemListController::SelectionBehavior, selectionBehavior); 0509 QFETCH(bool, groupingEnabled); 0510 QFETCH(Qt::LayoutDirection, layoutDirection); 0511 QFETCH(QList<keyPressViewStatePair>, testList); 0512 0513 QApplication::setLayoutDirection(layoutDirection); 0514 m_view->setLayoutDirection(layoutDirection); 0515 0516 m_view->setItemLayout(layout); 0517 QCOMPARE(m_view->itemLayout(), layout); 0518 0519 m_view->setScrollOrientation(scrollOrientation); 0520 QCOMPARE(m_view->scrollOrientation(), scrollOrientation); 0521 0522 m_controller->setSelectionBehavior(selectionBehavior); 0523 QCOMPARE(m_controller->selectionBehavior(), selectionBehavior); 0524 0525 m_model->setGroupedSorting(groupingEnabled); 0526 QCOMPARE(m_model->groupedSorting(), groupingEnabled); 0527 0528 adjustGeometryForColumnCount(columnCount); 0529 QCOMPARE(m_view->m_layouter->m_columnCount, columnCount); 0530 0531 QSignalSpy spySingleItemActivated(m_controller, &KItemListController::itemActivated); 0532 QSignalSpy spyMultipleItemsActivated(m_controller, &KItemListController::itemsActivated); 0533 0534 int rowCount = 0; 0535 while (!testList.isEmpty()) { 0536 ++rowCount; 0537 const QPair<KeyPress, ViewState> test = testList.takeFirst(); 0538 const Qt::Key key = test.first.m_key; 0539 const Qt::KeyboardModifiers modifier = test.first.m_modifier; 0540 const int current = test.second.m_current; 0541 const KItemSet selection = test.second.m_selection; 0542 const bool activated = test.second.m_activated; 0543 0544 QTest::keyClick(m_container, key, modifier); 0545 0546 QVERIFY2(m_selectionManager->currentItem() == current, 0547 qPrintable(QString("currentItem() returns index %1 but %2 would be expected. Before this, key \"%3\" was pressed. This test case is defined " 0548 "in row %4 of the testList from KItemListControllerTest::testKeyboardNavigation_data().") 0549 .arg(m_selectionManager->currentItem()) 0550 .arg(current) 0551 .arg(QKeySequence(key).toString()) 0552 .arg(rowCount))); 0553 switch (selectionBehavior) { 0554 case KItemListController::NoSelection: 0555 QVERIFY(m_selectionManager->selectedItems().isEmpty()); 0556 break; 0557 case KItemListController::SingleSelection: 0558 QCOMPARE(m_selectionManager->selectedItems(), KItemSet() << current); 0559 break; 0560 case KItemListController::MultiSelection: 0561 QCOMPARE(m_selectionManager->selectedItems(), selection); 0562 break; 0563 } 0564 0565 if (activated) { 0566 switch (selectionBehavior) { 0567 case KItemListController::MultiSelection: 0568 if (!selection.isEmpty()) { 0569 // The selected items should be activated. 0570 if (selection.count() == 1) { 0571 QVERIFY(!spySingleItemActivated.isEmpty()); 0572 QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), selection.first()); 0573 QVERIFY(spyMultipleItemsActivated.isEmpty()); 0574 } else { 0575 QVERIFY(spySingleItemActivated.isEmpty()); 0576 QVERIFY(!spyMultipleItemsActivated.isEmpty()); 0577 QCOMPARE(qvariant_cast<KItemSet>(spyMultipleItemsActivated.takeFirst().at(0)), selection); 0578 } 0579 break; 0580 } 0581 // No items are selected. Therefore, the current item should be activated. 0582 // This is handled by falling through to the NoSelection/SingleSelection case. 0583 Q_FALLTHROUGH(); 0584 case KItemListController::NoSelection: 0585 case KItemListController::SingleSelection: 0586 // In NoSelection and SingleSelection mode, the current item should be activated. 0587 QVERIFY(!spySingleItemActivated.isEmpty()); 0588 QCOMPARE(qvariant_cast<int>(spySingleItemActivated.takeFirst().at(0)), current); 0589 QVERIFY(spyMultipleItemsActivated.isEmpty()); 0590 break; 0591 } 0592 } 0593 } 0594 } 0595 0596 void KItemListControllerTest::testMouseClickActivation() 0597 { 0598 m_view->setItemLayout(KFileItemListView::IconsLayout); 0599 0600 // Make sure that we have a large window, such that 0601 // the items are visible and clickable. 0602 adjustGeometryForColumnCount(5); 0603 0604 // Make sure that the first item is visible in the view. 0605 m_view->setScrollOffset(0); 0606 QCOMPARE(m_view->firstVisibleIndex(), 0); 0607 0608 const QPointF pos = m_view->itemContextRect(0).center(); 0609 0610 // Save the "single click" setting. 0611 const bool restoreSettingsSingleClick = m_testStyle->activateItemOnSingleClick(); 0612 0613 QGraphicsSceneMouseEvent mousePressEvent(QEvent::GraphicsSceneMousePress); 0614 mousePressEvent.setPos(pos); 0615 mousePressEvent.setButton(Qt::LeftButton); 0616 mousePressEvent.setButtons(Qt::LeftButton); 0617 0618 QGraphicsSceneMouseEvent mouseReleaseEvent(QEvent::GraphicsSceneMouseRelease); 0619 mouseReleaseEvent.setPos(pos); 0620 mouseReleaseEvent.setButton(Qt::LeftButton); 0621 mouseReleaseEvent.setButtons(Qt::NoButton); 0622 0623 QSignalSpy spyItemActivated(m_controller, &KItemListController::itemActivated); 0624 0625 // Default setting: single click activation. 0626 m_testStyle->setActivateItemOnSingleClick(true); 0627 m_view->event(&mousePressEvent); 0628 m_view->event(&mouseReleaseEvent); 0629 QCOMPARE(spyItemActivated.count(), 1); 0630 spyItemActivated.clear(); 0631 0632 // Set the global setting to "double click activation". 0633 m_testStyle->setActivateItemOnSingleClick(false); 0634 m_view->event(&mousePressEvent); 0635 m_view->event(&mouseReleaseEvent); 0636 QCOMPARE(spyItemActivated.count(), 0); 0637 spyItemActivated.clear(); 0638 0639 // Enforce single click activation in the controller. 0640 m_controller->setSingleClickActivationEnforced(true); 0641 m_view->event(&mousePressEvent); 0642 m_view->event(&mouseReleaseEvent); 0643 QCOMPARE(spyItemActivated.count(), 1); 0644 spyItemActivated.clear(); 0645 0646 // Do not enforce single click activation in the controller. 0647 m_controller->setSingleClickActivationEnforced(false); 0648 m_view->event(&mousePressEvent); 0649 m_view->event(&mouseReleaseEvent); 0650 QCOMPARE(spyItemActivated.count(), 0); 0651 spyItemActivated.clear(); 0652 0653 // Set the global setting back to "single click activation". 0654 m_testStyle->setActivateItemOnSingleClick(true); 0655 m_view->event(&mousePressEvent); 0656 m_view->event(&mouseReleaseEvent); 0657 QCOMPARE(spyItemActivated.count(), 1); 0658 spyItemActivated.clear(); 0659 0660 // Enforce single click activation in the controller. 0661 m_controller->setSingleClickActivationEnforced(true); 0662 m_view->event(&mousePressEvent); 0663 m_view->event(&mouseReleaseEvent); 0664 QCOMPARE(spyItemActivated.count(), 1); 0665 spyItemActivated.clear(); 0666 0667 // Restore previous settings. 0668 m_controller->setSingleClickActivationEnforced(true); 0669 m_testStyle->setActivateItemOnSingleClick(restoreSettingsSingleClick); 0670 } 0671 0672 void KItemListControllerTest::adjustGeometryForColumnCount(int count) 0673 { 0674 const QSize size = m_view->itemSize().toSize(); 0675 0676 QRect rect = m_container->geometry(); 0677 rect.setSize(size * count); 0678 m_container->setGeometry(rect); 0679 0680 // Increase the size of the container until the correct column count is reached. 0681 while (m_view->m_layouter->m_columnCount < count) { 0682 rect = m_container->geometry(); 0683 rect.setSize(rect.size() + size); 0684 m_container->setGeometry(rect); 0685 } 0686 } 0687 0688 QTEST_MAIN(KItemListControllerTest) 0689 0690 #include "kitemlistcontrollertest.moc"