diff --git a/.gitignore b/.gitignore index 9ab62e1908..9ffa696ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,6 @@ desktop.ini # MSVC Files CMakeSettings.json CMakePresets.json +CMakeUserPresets.json .vs/ -out/ \ No newline at end of file +out/ diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 82963af445..04ce9a499e 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -3840,6 +3840,10 @@ Error: %1 Has TOTP + + Background Color + + EntryPreviewWidget diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 877efe9d2b..234a08ba7e 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -116,7 +116,7 @@ int EntryModel::columnCount(const QModelIndex& parent) const return 0; } - return 15; + return 16; } QVariant EntryModel::data(const QModelIndex& index, int role) const @@ -230,6 +230,13 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const return result; } + case Color: + QColor backgroundColor; + backgroundColor.setNamedColor(entry->backgroundColor()); + if (backgroundColor.isValid()) { + result = "▍"; + return result; + } } } else if (role == Qt::UserRole) { // Qt::UserRole is used as sort role, see EntryView::EntryView() switch (index.column()) { @@ -314,6 +321,15 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const } return font; } else if (role == Qt::ForegroundRole) { + + if (index.column() == Color) { + QColor backgroundColor; + backgroundColor.setNamedColor(entry->backgroundColor()); + if (backgroundColor.isValid()) { + return backgroundColor; + } + } + QColor foregroundColor; foregroundColor.setNamedColor(entry->foregroundColor()); if (entry->hasReferences()) { @@ -327,10 +343,12 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const return QVariant(foregroundColor); } } else if (role == Qt::BackgroundRole) { - QColor backgroundColor; - backgroundColor.setNamedColor(entry->backgroundColor()); - if (backgroundColor.isValid()) { - return QVariant(backgroundColor); + if (m_backgroundColorVisible) { + QColor backgroundColor; + backgroundColor.setNamedColor(entry->backgroundColor()); + if (backgroundColor.isValid()) { + return QVariant(backgroundColor); + } } } else if (role == Qt::ToolTipRole) { if (index.column() == PasswordStrength && !entry->password().isEmpty() && !entry->excludeFromReports()) { @@ -414,6 +432,8 @@ QVariant EntryModel::headerData(int section, Qt::Orientation orientation, int ro return tr("Has attachments"); case Totp: return tr("Has TOTP"); + case Color: + return tr("Background Color"); } } @@ -596,3 +616,7 @@ void EntryModel::makeConnections(const Group* group) connect(group, SIGNAL(entryMovedDown()), SLOT(entryMovedDown())); connect(group, SIGNAL(entryDataChanged(Entry*)), SLOT(entryDataChanged(Entry*))); } +void EntryModel::setBackgroundColorVisible(bool visible) +{ + m_backgroundColorVisible = visible; +} diff --git a/src/gui/entry/EntryModel.h b/src/gui/entry/EntryModel.h index 8e79be3841..4ad81f4c48 100644 --- a/src/gui/entry/EntryModel.h +++ b/src/gui/entry/EntryModel.h @@ -48,7 +48,8 @@ class EntryModel : public QAbstractTableModel Attachments = 11, Totp = 12, Size = 13, - PasswordStrength = 14 + PasswordStrength = 14, + Color = 15 }; explicit EntryModel(QObject* parent = nullptr); @@ -67,6 +68,7 @@ class EntryModel : public QAbstractTableModel void setGroup(Group* group); void setEntries(const QList& entries); + void setBackgroundColorVisible(bool visible); private slots: void entryAboutToAdd(Entry* entry); @@ -85,6 +87,7 @@ private slots: void severConnections(); void makeConnections(const Group* group); + bool m_backgroundColorVisible = true; Group* m_group; QList m_entries; QList m_orgEntries; diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 868250a666..c587f7c1ea 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -219,11 +219,12 @@ void EntryView::displaySearch(const QList& entries) m_model->setEntries(entries); header()->showSection(EntryModel::ParentGroup); + setFirstEntryActive(); + // Reset sort column to 'Group', overrides DatabaseWidgetStateSync m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); - setFirstEntryActive(); m_inSearchMode = true; } @@ -335,6 +336,7 @@ bool EntryView::setViewState(const QByteArray& state) bool status = header()->restoreState(state); resetFixedColumns(); m_columnsNeedRelayout = state.isEmpty(); + onHeaderChanged(); return status; } @@ -375,6 +377,9 @@ void EntryView::toggleColumnVisibility(QAction* action) // least one visible column remains, as the table header will disappear // entirely when all columns are hidden int columnIndex = action->data().toInt(); + if (columnIndex == EntryModel::Color) { + m_model->setBackgroundColorVisible(!action->isChecked()); + } if (action->isChecked()) { header()->showSection(columnIndex); if (header()->sectionSize(columnIndex) == 0) { @@ -446,6 +451,8 @@ void EntryView::resetFixedColumns() header()->resizeSection(col, width); } } + header()->setMinimumSectionSize(1); + header()->resizeSection(EntryModel::Color, ICON_ONLY_SECTION_SIZE); } /** @@ -474,6 +481,8 @@ void EntryView::resetViewToDefaults() header()->hideSection(EntryModel::Attachments); header()->hideSection(EntryModel::Size); header()->hideSection(EntryModel::PasswordStrength); + header()->hideSection(EntryModel::Color); + onHeaderChanged(); // Reset column order to logical indices for (int i = 0; i < header()->count(); ++i) { @@ -501,6 +510,11 @@ void EntryView::resetViewToDefaults() } } +void EntryView::onHeaderChanged() +{ + m_model->setBackgroundColorVisible(isColumnHidden(EntryModel::Color)); +} + void EntryView::showEvent(QShowEvent* event) { QTreeView::showEvent(event); diff --git a/src/gui/entry/EntryView.h b/src/gui/entry/EntryView.h index 3a0cc1d60d..759097b346 100644 --- a/src/gui/entry/EntryView.h +++ b/src/gui/entry/EntryView.h @@ -76,6 +76,7 @@ private slots: private: void resetFixedColumns(); bool isColumnHidden(int logicalIndex); + void onHeaderChanged(); EntryModel* const m_model; SortFilterHideProxyModel* const m_sortModel; diff --git a/tests/TestEntryModel.cpp b/tests/TestEntryModel.cpp index b7b3473d78..3506426184 100644 --- a/tests/TestEntryModel.cpp +++ b/tests/TestEntryModel.cpp @@ -313,15 +313,11 @@ void TestEntryModel::testProxyModel() modelSource->setGroup(db->rootGroup()); - /** - * @author Fonic - * Update comparison value of modelProxy->columnCount() to account for - * additional columns 'Password', 'Notes', 'Expires', 'Created', 'Modified', - * 'Accessed', 'Paperclip', 'Attachments', and TOTP - */ + // Test hiding and showing a column + auto columnCount = modelProxy->columnCount(); QSignalSpy spyColumnRemove(modelProxy, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int))); modelProxy->hideColumn(0, true); - QCOMPARE(modelProxy->columnCount(), 14); + QCOMPARE(modelProxy->columnCount(), columnCount - 1); QVERIFY(!spyColumnRemove.isEmpty()); int oldSpyColumnRemoveSize = spyColumnRemove.size(); @@ -335,15 +331,9 @@ void TestEntryModel::testProxyModel() entryList << entry; modelSource->setEntries(entryList); - /** - * @author Fonic - * Update comparison value of modelProxy->columnCount() to account for - * additional columns 'Password', 'Notes', 'Expires', 'Created', 'Modified', - * 'Accessed', 'Paperclip', 'Attachments', and TOTP - */ QSignalSpy spyColumnInsert(modelProxy, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int))); modelProxy->hideColumn(0, false); - QCOMPARE(modelProxy->columnCount(), 15); + QCOMPARE(modelProxy->columnCount(), columnCount); QVERIFY(!spyColumnInsert.isEmpty()); int oldSpyColumnInsertSize = spyColumnInsert.size(); diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 170bf3a175..4840092ea6 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -1082,6 +1082,13 @@ void TestGui::testSearch() QCOMPARE(groupView->currentGroup(), m_db->rootGroup()); QVERIFY(!m_dbWidget->isSearchActive()); + // check if first entry is selected after search + QTest::keyClicks(searchTextEdit, "some"); + QTRY_VERIFY(m_dbWidget->isSearchActive()); + QTRY_COMPARE(entryView->selectedEntries().length(), 1); + QModelIndex index_current = entryView->indexFromEntry(entryView->currentEntry()); + QTRY_COMPARE(index_current.row(), 0); + // Try to edit the first entry from the search view // Refocus back to search edit QTest::mouseClick(searchTextEdit, Qt::LeftButton);