Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve library sidebar UX (right-click and selection after add, rename, delete, duplicate etc.) #11208

Merged
merged 45 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bd9656d
Library playlist features: use kInvalidPlaylistId instead of -1
ronso0 Jan 24, 2023
bbe8112
BasePlaylistFeature: move DAO connections to separate function
ronso0 Jan 24, 2023
9748e36
Library sidebar/features: some fixes, minor cleanup, improve comments
ronso0 Jan 24, 2023
74ec98f
WLibrarySidebar::keyPressEvent: ensure new selection is visible
ronso0 Jan 28, 2023
443b174
Playlists/Crates: use last right-clicked id for playlist file import
ronso0 Jan 28, 2023
b576824
Library sidebar: don't select on right-click, only set focus
ronso0 Jan 24, 2023
2f8681d
Library sidebar: don't select newly created or duplicated crate/playlist
ronso0 Jan 26, 2023
def2ddf
Library sidebar: don't rebuild tree model after item rename or lock c…
ronso0 Jan 27, 2023
aabd985
Library sidebar: track left & right clicks separately
ronso0 Jan 26, 2023
82da0eb
BasePlaylistFeature::activatePlaylist: remove redundant selectIndex()
ronso0 Jan 28, 2023
5a696b8
CrateFeature::activateCrate: remove redundant activateChild()
ronso0 Jan 28, 2023
fa9278b
History feature: fix decoration of current playlist after 'Finish cur…
ronso0 Jan 28, 2023
8f8bf48
Library sidebar: add item selection helpers for playlists & crates
ronso0 Jan 26, 2023
60198d5
History feature: clear view when clicking YEAR items
ronso0 Jan 26, 2023
117f61a
History feature: allow deleting all playlists in YEAR node
ronso0 Jan 26, 2023
42ce1d4
PlaylistDAO: add setPlaylistsLocked
ronso0 Feb 9, 2023
b046cae
History: allow un/locking all playlists in a YEAR folder
ronso0 Feb 9, 2023
302215e
PlaylistDAO: add getNextPlaylist(), to be used for History feature
ronso0 Jan 26, 2023
2aebd75
Library sidebar: handle item selection in DAO callback slots ...
ronso0 Jan 28, 2023
8740178
Library sidebar: add helper to get the (first) selected index
ronso0 Feb 6, 2023
bf28a07
Library sidebar: toggleItem() emits click(), not delayed pressed()
ronso0 Feb 6, 2023
693f8a4
BasePlaylistFeature::updateChildModel: use QSet to reduce tree iterat…
ronso0 Feb 9, 2023
013cbc3
skins: add focus indicator (right-click) to library sidebar items
ronso0 Feb 10, 2023
2918884
BasePlaylistFeature: improve playlist import comment
ronso0 Mar 22, 2023
35a0905
simplify WLibrarySidebar::selectedIndex return
ronso0 Mar 22, 2023
4fe9156
playlist features: remove obsolete qAsConst
ronso0 Mar 22, 2023
40dd91a
PlaylistDAO: use kInvalidPlaylistId
ronso0 Mar 22, 2023
5c2b1f1
PlaylistDAO: simplify string building for Sql query
ronso0 Mar 23, 2023
6c56d35
CrateFeature: use existing table model for importing when crate is se…
ronso0 Mar 23, 2023
2bc3884
PlaylistFeature: early return for inedaquate playlist type
ronso0 Mar 23, 2023
4a2be9b
playlist features: fix debug string
ronso0 Mar 23, 2023
150a116
PlaylistDAO: make delete functions return bool
ronso0 Mar 23, 2023
59be421
PlaylistDAO: remove type input from deletePlaylists()
ronso0 Mar 23, 2023
6e3cc8e
CrateFeature: explicit cast to int ('possible loss of data' warning)
ronso0 Mar 23, 2023
f89e170
SetlogFeature: add translator comments, re/move debug messages
ronso0 Mar 23, 2023
7b84778
History: bulk-delete only unlocked playlists
ronso0 Mar 23, 2023
85c9209
Merge remote-tracking branch 'mixxx/2.4' into lib-sidebar-right-click…
ronso0 Apr 23, 2023
be08c7d
PlaylistDAO: std::move QStringList to deleteUnlockedPlaylists()
ronso0 Apr 23, 2023
cdc0bf6
PlaylistDAO: correct return value of deleteAllUnlockedPlaylistsWithFe…
ronso0 Apr 23, 2023
223c610
History feature: improve 'Delete unlocked year playlists' dialogs
ronso0 Apr 23, 2023
850ecdd
PlaylistDAO: remove unnecessary/confusing comments
ronso0 Apr 24, 2023
f01647f
BasePlaylistFeature: don't wipe track count & duration when selecting
ronso0 May 20, 2023
89b3aff
Playlists: restore resorting sidebar items after playlist rename
ronso0 May 20, 2023
4cdc077
Crates: restore resorting sidebar items after playlist rename
ronso0 May 20, 2023
877f112
CrateFeature::activateCrate: just return false if crate doesn't exist
ronso0 May 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Library sidebar/features: some fixes, minor cleanup, improve comments
  • Loading branch information
ronso0 committed Mar 21, 2023
commit 9748e3618d2b61e34e8524a74a84e3e4057d355a
2 changes: 0 additions & 2 deletions src/library/dao/playlistdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,6 @@ void PlaylistDAO::addPlaylistToAutoDJQueue(const int playlistId, AutoDJSendLoc l
}

int PlaylistDAO::getPreviousPlaylist(const int currentPlaylistId, HiddenType hidden) const {
// Find out the highest position existing in the playlist so we know what
// position this track should have.
QSqlQuery query(m_database);
query.prepare(QStringLiteral(
"SELECT max(id) as id FROM Playlists "
Expand Down
21 changes: 10 additions & 11 deletions src/library/sidebarmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

namespace {

// The time between selecting and activating (= clicking) a feature item
// in the sidebar tree. This is essential to allow smooth scrolling through
// a list of items with an encoder or the keyboard! A value of 300 ms has
// been chosen as a compromise between usability and responsiveness.
/// The time between selecting and activating (= clicking) a feature item
/// in the sidebar tree. This is essential to allow smooth scrolling through
/// a list of items with an encoder or the keyboard! A value of 300 ms has
/// been chosen as a compromise between usability and responsiveness.
constexpr int kPressedUntilClickedTimeoutMillis = 300;

} // anonymous namespace
Expand Down Expand Up @@ -290,6 +290,8 @@ void SidebarModel::slotPressedUntilClickedTimeout() {
}
}

/// Connected to WLibrarySidebar::pressed signal, called after left click,
/// right click and selection change via keyboard or controller
void SidebarModel::pressed(const QModelIndex& index) {
stopPressedUntilClickedTimer();
if (index.isValid()) {
Expand Down Expand Up @@ -317,6 +319,7 @@ void SidebarModel::clicked(const QModelIndex& index) {
}
}

/// Invoked by double click and click on tree node expand icons
void SidebarModel::doubleClicked(const QModelIndex& index) {
stopPressedUntilClickedTimer();
if (index.isValid()) {
Expand All @@ -337,9 +340,7 @@ void SidebarModel::rightClicked(const QPoint& globalPos, const QModelIndex& inde
if (index.isValid()) {
if (index.internalPointer() == this) {
m_sFeatures[index.row()]->onRightClick(globalPos);
}
else
{
} else {
TreeItem* pTreeItem = static_cast<TreeItem*>(index.internalPointer());
if (pTreeItem) {
LibraryFeature* pFeature = pTreeItem->feature();
Expand Down Expand Up @@ -406,7 +407,7 @@ bool SidebarModel::dropAccept(const QModelIndex& index, const QList<QUrl>& urls,

bool SidebarModel::hasTrackTable(const QModelIndex& index) const {
if (index.internalPointer() == this) {
return m_sFeatures[index.row()]->hasTrackTable();
return m_sFeatures[index.row()]->hasTrackTable();
}
return false;
}
Expand All @@ -429,7 +430,7 @@ bool SidebarModel::dragMoveAccept(const QModelIndex& index, const QUrl& url) {
return result;
}

// Translates an index from the child models to an index of the sidebar models
/// Translates an index from the child models to an index of the sidebar models
QModelIndex SidebarModel::translateSourceIndex(const QModelIndex& index) {
/* These method is called from the slot functions below.
* QObject::sender() return the object which emitted the signal
Expand All @@ -454,8 +455,6 @@ QModelIndex SidebarModel::translateIndex(
TreeItem* pItem = static_cast<TreeItem*>(index.internalPointer());
translatedIndex = createIndex(index.row(), index.column(), pItem);
} else {
//Comment from Tobias Rafreider --> Dead Code????

for (int i = 0; i < m_sFeatures.size(); ++i) {
if (m_sFeatures[i]->sidebarModel() == pModel) {
translatedIndex = createIndex(i, 0, this);
Expand Down
16 changes: 9 additions & 7 deletions src/library/trackset/baseplaylistfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ void BasePlaylistFeature::selectPlaylistInSidebar(int playlistId, bool select) {
return;
}
QModelIndex index = indexFromPlaylistId(playlistId);
if (index.isValid() && m_pSidebarWidget) {
if (index.isValid()) {
m_pSidebarWidget->selectChildIndex(index, select);
}
}
Expand Down Expand Up @@ -246,7 +246,7 @@ void BasePlaylistFeature::slotRenamePlaylist() {

if (locked) {
qDebug() << "Skipping playlist rename because playlist" << playlistId
<< "is locked.";
<< oldName << "is locked.";
return;
}
QString newName;
Expand Down Expand Up @@ -585,7 +585,7 @@ void BasePlaylistFeature::slotExportPlaylist() {
"mixxx.db.model.playlist_export"));

emit saveModelState();
pPlaylistTableModel->setTableModel(m_pPlaylistTableModel->getPlaylist());
pPlaylistTableModel->setTableModel(playlistId);
pPlaylistTableModel->setSort(
pPlaylistTableModel->fieldIndex(
ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION),
Expand Down Expand Up @@ -621,13 +621,17 @@ void BasePlaylistFeature::slotExportPlaylist() {
}

void BasePlaylistFeature::slotExportTrackFiles() {
int playlistId = playlistIdFromIndex(m_lastRightClickedIndex);
if (playlistId == kInvalidPlaylistId) {
return;
}
QScopedPointer<PlaylistTableModel> pPlaylistTableModel(
new PlaylistTableModel(this,
m_pLibrary->trackCollectionManager(),
"mixxx.db.model.playlist_export"));

emit saveModelState();
pPlaylistTableModel->setTableModel(m_pPlaylistTableModel->getPlaylist());
pPlaylistTableModel->setTableModel(playlistId);
pPlaylistTableModel->setSort(pPlaylistTableModel->fieldIndex(
ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_POSITION),
Qt::AscendingOrder);
Expand Down Expand Up @@ -732,9 +736,7 @@ void BasePlaylistFeature::updateChildModel(int playlistId) {
}
}

/**
* Clears the child model dynamically, but the invisible root item remains
*/
/// Clears the child model dynamically, but the invisible root item remains
void BasePlaylistFeature::clearChildModel() {
m_lastRightClickedIndex = QModelIndex();
m_pSidebarModel->removeRows(0, m_pSidebarModel->rowCount());
Expand Down
17 changes: 9 additions & 8 deletions src/library/trackset/crate/cratefeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,19 @@ void CrateFeature::connectLibrary(Library* pLibrary) {
}

void CrateFeature::connectTrackCollection() {
connect(m_pTrackCollection,
connect(m_pTrackCollection, // created new, duplicated or imported playlist to new crate
&TrackCollection::crateInserted,
this,
&CrateFeature::slotCrateTableChanged);
connect(m_pTrackCollection,
connect(m_pTrackCollection, // renamed, un/locked, toggled AutoDJ source
&TrackCollection::crateUpdated,
this,
&CrateFeature::slotCrateTableChanged);
connect(m_pTrackCollection,
&TrackCollection::crateDeleted,
this,
&CrateFeature::slotCrateTableChanged);
connect(m_pTrackCollection,
connect(m_pTrackCollection, // crate tracks hidden, unhidden or purged
&TrackCollection::crateTracksChanged,
this,
&CrateFeature::slotCrateContentChanged);
Expand Down Expand Up @@ -723,10 +723,10 @@ void CrateFeature::slotAnalyzeCrate() {
}

void CrateFeature::slotExportPlaylist() {
CrateId crateId = m_crateTableModel.selectedCrate();
CrateId crateId = crateIdFromIndex(m_lastRightClickedIndex);
Crate crate;
if (m_pTrackCollection->crates().readCrateById(crateId, &crate)) {
qDebug() << "Exporting crate" << crate;
qDebug() << "Exporting crate" << crateId << crate;
} else {
qDebug() << "Failed to export crate" << crateId;
}
Expand Down Expand Up @@ -768,7 +768,7 @@ void CrateFeature::slotExportPlaylist() {
// Create a new table model since the main one might have an active search.
QScopedPointer<CrateTableModel> pCrateTableModel(
new CrateTableModel(this, m_pLibrary->trackCollectionManager()));
pCrateTableModel->selectCrate(m_crateTableModel.selectedCrate());
pCrateTableModel->selectCrate(crateId);
pCrateTableModel->select();

if (fileLocation.endsWith(".csv", Qt::CaseInsensitive)) {
Expand All @@ -780,8 +780,8 @@ void CrateFeature::slotExportPlaylist() {
QList<QString> playlistItems;
int rows = pCrateTableModel->rowCount();
for (int i = 0; i < rows; ++i) {
QModelIndex index = m_crateTableModel.index(i, 0);
playlistItems << m_crateTableModel.getTrackLocation(index);
QModelIndex index = pCrateTableModel->index(i, 0);
playlistItems << pCrateTableModel->getTrackLocation(index);
}
exportPlaylistItemsIntoFile(
fileLocation,
Expand Down Expand Up @@ -810,6 +810,7 @@ void CrateFeature::slotExportTrackFiles() {

void CrateFeature::storePrevSiblingCrateId(CrateId crateId) {
QModelIndex actIndex = indexFromCrateId(crateId);
m_prevSiblingCrate = CrateId();
for (int i = (actIndex.row() + 1); i >= (actIndex.row() - 1); i -= 2) {
QModelIndex newIndex = actIndex.sibling(i, actIndex.column());
if (newIndex.isValid()) {
Expand Down
13 changes: 5 additions & 8 deletions src/library/trackset/setlogfeature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,8 @@ SetlogFeature::SetlogFeature(
}

SetlogFeature::~SetlogFeature() {
// If the history playlist we created doesn't have any tracks in it then
// delete it so we don't end up with tons of empty playlists. This is mostly
// for developers since they regularly open Mixxx without loading a track.
if (m_playlistId != kInvalidPlaylistId &&
m_playlistDao.tracksInPlaylist(m_playlistId) == 0) {
m_playlistDao.deletePlaylist(m_playlistId);
}
// Also clean history up when shutting down in case the track threshold changed
// Clean up history when shutting down in case the track threshold changed,
// incl. potentially empty current playlist
deleteAllUnlockedPlaylistsWithFewerTracks();
}

Expand Down Expand Up @@ -212,6 +206,8 @@ QModelIndex SetlogFeature::constructChildModel(int selectedId) {
.toDateTime();

// Create the TreeItem whose parent is the invisible root item
// Show only [kNumToplevelHistoryEntries -1] recent playlists at the top
// level before grouping them by year.
if (row >= kNumToplevelHistoryEntries) {
int yearCreated = dateCreated.date().year();

Expand Down Expand Up @@ -280,6 +276,7 @@ void SetlogFeature::decorateChild(TreeItem* item, int playlistId) {
}
}

/// Invoked on startup to create new current playlist and by "Finish current and start new"
void SetlogFeature::slotGetNewPlaylist() {
//qDebug() << "slotGetNewPlaylist() successfully triggered !";

Expand Down
16 changes: 9 additions & 7 deletions src/widget/wlibrarysidebar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void WLibrarySidebar::dropEvent(QDropEvent * event) {
}

void WLibrarySidebar::toggleSelectedItem() {
QModelIndexList selectedIndices = this->selectionModel()->selectedRows();
QModelIndexList selectedIndices = selectionModel()->selectedRows();
if (selectedIndices.size() > 0) {
QModelIndex index = selectedIndices.at(0);
// Activate the item so its content shows in the main library.
Expand All @@ -184,7 +184,7 @@ void WLibrarySidebar::toggleSelectedItem() {
}

bool WLibrarySidebar::isLeafNodeSelected() {
QModelIndexList selectedIndices = this->selectionModel()->selectedRows();
QModelIndexList selectedIndices = selectionModel()->selectedRows();
if (selectedIndices.size() > 0) {
QModelIndex index = selectedIndices.at(0);
if(!index.model()->hasChildren(index)) {
Expand All @@ -198,6 +198,8 @@ bool WLibrarySidebar::isLeafNodeSelected() {
return false;
}

/// Invoked by actual keypresses (requires widget focus) and emulated keypresses
/// sent by LibraryControl
void WLibrarySidebar::keyPressEvent(QKeyEvent* event) {
switch (event->key()) {
case Qt::Key_Return:
Expand Down Expand Up @@ -254,39 +256,39 @@ void WLibrarySidebar::keyPressEvent(QKeyEvent* event) {
emit setLibraryFocus(FocusWidget::TracksTable);
return;
case kRenameSidebarItemShortcutKey: { // F2
// Rename crate or playlist (internal, external, history
// Rename crate or playlist (internal, external, history)
QModelIndexList selectedIndices = selectionModel()->selectedRows();
if (selectedIndices.isEmpty()) {
return;
}
// If an expanded item is selected let QTreeView collapse it
QModelIndex selIndex = selectedIndices.first();
VERIFY_OR_DEBUG_ASSERT(selIndex.isValid()) {
qDebug() << "invalid sidebar index";
return;
}
if (isExpanded(selIndex)) {
// Root views / knots can not be renamed
return;
}
emit renameItem(selIndex);
return;
}
case kHideRemoveShortcutKey: { // Del / Cmd+Backspace deletes items
// Rename crate or playlist (internal, external, history
case kHideRemoveShortcutKey: { // Del (macOS: Cmd+Backspace)
// Delete crate or playlist (internal, external, history)
if (event->modifiers() != kHideRemoveShortcutModifier) {
return;
}
QModelIndexList selectedIndices = selectionModel()->selectedRows();
if (selectedIndices.isEmpty()) {
return;
}
// If an expanded item is selected let QTreeView collapse it
QModelIndex selIndex = selectedIndices.first();
VERIFY_OR_DEBUG_ASSERT(selIndex.isValid()) {
qDebug() << "invalid sidebar index";
return;
}
if (isExpanded(selIndex)) {
// Root views / knots can not be deleted
return;
}
emit deleteItem(selIndex);
Expand Down