-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Usability/Keyboard: Make the star ratings in the tracks view editable via keyboard #13717
base: main
Are you sure you want to change the base?
Changes from 12 commits
7bfa981
f843e42
9fe4bb7
84306eb
ea05b05
db4b275
bff6003
b5dae7e
3a0cfc5
18eebb4
c59a384
05e3874
2832fc5
caa5d25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,16 +1,32 @@ | ||||||||||
#include "library/tabledelegates/stardelegate.h" | ||||||||||
|
||||||||||
#include <QTableView> | ||||||||||
#include <QTimer> | ||||||||||
|
||||||||||
#include "library/starrating.h" | ||||||||||
#include "library/tabledelegates/stareditor.h" | ||||||||||
#include "library/tabledelegates/tableitemdelegate.h" | ||||||||||
#include "moc_stardelegate.cpp" | ||||||||||
#include "widget/wtracktableview.h" | ||||||||||
|
||||||||||
StarDelegate::StarDelegate(QTableView* pTableView) | ||||||||||
: TableItemDelegate(pTableView), | ||||||||||
m_isOneCellInEditMode(false) { | ||||||||||
m_persistentEditorState(PersistentEditor_NotOpen) { | ||||||||||
connect(this, &QAbstractItemDelegate::closeEditor, this, &StarDelegate::closingEditor); | ||||||||||
connect(pTableView, &QTableView::entered, this, &StarDelegate::cellEntered); | ||||||||||
connect(pTableView, &QTableView::viewportEntered, this, &StarDelegate::cursorNotOverAnyCell); | ||||||||||
|
||||||||||
auto* pTrackTableView = qobject_cast<WTrackTableView*>(pTableView); | ||||||||||
if (pTrackTableView) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to always used in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll do you one better: mixxx/src/library/basetracktablemodel.cpp Lines 478 to 481 in 68d3796
|
||||||||||
connect(pTrackTableView, | ||||||||||
&WTrackTableView::viewportLeaving, | ||||||||||
this, | ||||||||||
&StarDelegate::cursorNotOverAnyCell); | ||||||||||
connect(pTrackTableView, | ||||||||||
&WTrackTableView::editRequested, | ||||||||||
this, | ||||||||||
&StarDelegate::editRequested); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::paintItem( | ||||||||||
|
@@ -43,8 +59,15 @@ QWidget* StarDelegate::createEditor(QWidget* parent, | |||||||||
QStyleOptionViewItem newOption = option; | ||||||||||
initStyleOption(&newOption, index); | ||||||||||
|
||||||||||
StarEditor* editor = | ||||||||||
new StarEditor(parent, m_pTableView, index, newOption, m_focusBorderColor); | ||||||||||
StarEditor* editor = new StarEditor(parent, | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When using Qt fat pointer, we usually used our (Nit: rename
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, please use the pointer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do. I just didn't have the time to update the code yet 😅 |
||||||||||
m_pTableView, | ||||||||||
index, | ||||||||||
newOption, | ||||||||||
m_persistentEditorState != PersistentEditor_Opening); | ||||||||||
|
||||||||||
editor->setObjectName("LibraryStarEditor"); | ||||||||||
editor->ensurePolished(); | ||||||||||
|
||||||||||
connect(editor, | ||||||||||
&StarEditor::editingFinished, | ||||||||||
this, | ||||||||||
|
@@ -68,24 +91,111 @@ void StarDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, | |||||||||
void StarDelegate::commitAndCloseEditor() { | ||||||||||
StarEditor* editor = qobject_cast<StarEditor*>(sender()); | ||||||||||
emit commitData(editor); | ||||||||||
emit closeEditor(editor); | ||||||||||
emit closeEditor(editor, QAbstractItemDelegate::SubmitModelCache); | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::closingEditor(QWidget* widget, QAbstractItemDelegate::EndEditHint hint) { | ||||||||||
Q_UNUSED(hint); | ||||||||||
cr7pt0gr4ph7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
auto* editor = qobject_cast<StarEditor*>(widget); | ||||||||||
VERIFY_OR_DEBUG_ASSERT(editor) { | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
restorePersistentRatingEditor(editor->getModelIndex()); | ||||||||||
cr7pt0gr4ph7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::editRequested(const QModelIndex& index, | ||||||||||
QAbstractItemView::EditTrigger trigger, | ||||||||||
QEvent* event) { | ||||||||||
Q_UNUSED(event); | ||||||||||
cr7pt0gr4ph7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
|
||||||||||
// This slot is called when an edit is requested for ANY cell on the | ||||||||||
// QTableView but the code should only be executed on a column with a | ||||||||||
// StarRating. | ||||||||||
if (trigger == QAbstractItemView::EditTrigger::EditKeyPressed && | ||||||||||
m_persistentEditorState == PersistentEditor_Open && | ||||||||||
index.data().canConvert<StarRating>() && | ||||||||||
m_currentEditedCellIndex == index) { | ||||||||||
// Close the (implicit) persistent editor for the current cell, | ||||||||||
// so that a new explicit editor can be opened instead. | ||||||||||
closeCurrentPersistentRatingEditor(true); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::cellEntered(const QModelIndex& index) { | ||||||||||
// This slot is called if the mouse pointer enters ANY cell on the | ||||||||||
// QTableView but the code should only be executed on a column with a | ||||||||||
// StarRating. | ||||||||||
if (index.data().canConvert<StarRating>()) { | ||||||||||
if (m_isOneCellInEditMode) { | ||||||||||
// Don't close other editors when hovering the stars cell! | ||||||||||
m_pTableView->closePersistentEditor(m_currentEditedCellIndex); | ||||||||||
} | ||||||||||
m_pTableView->openPersistentEditor(index); | ||||||||||
m_isOneCellInEditMode = true; | ||||||||||
m_currentEditedCellIndex = index; | ||||||||||
} else if (m_isOneCellInEditMode) { | ||||||||||
m_isOneCellInEditMode = false; | ||||||||||
openPersistentRatingEditor(index); | ||||||||||
} else { | ||||||||||
closeCurrentPersistentRatingEditor(false); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::cursorNotOverAnyCell() { | ||||||||||
// Invoked when the mouse cursor is not over any specific cell, | ||||||||||
// or when the mouse cursor has left the table area | ||||||||||
closeCurrentPersistentRatingEditor(false); | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::openPersistentRatingEditor(const QModelIndex& index) { | ||||||||||
// Qt6: Check whether a non-persistent editor exists at index. | ||||||||||
// QTableView::closePersistentEditor() would also close | ||||||||||
// a non-persistent editor, so we have to make sure to | ||||||||||
// not call it if an editor already exists. | ||||||||||
if (m_pTableView->indexWidget(index)) { | ||||||||||
return; | ||||||||||
} | ||||||||||
|
||||||||||
// Close the previously open persistent rating editor | ||||||||||
if (m_persistentEditorState == PersistentEditor_Open) { | ||||||||||
// Don't close other editors when hovering the stars cell! | ||||||||||
m_pTableView->closePersistentEditor(m_currentEditedCellIndex); | ||||||||||
} | ||||||||||
|
||||||||||
m_persistentEditorState = PersistentEditor_NotOpen; | ||||||||||
m_persistentEditorState = PersistentEditor_Opening; | ||||||||||
m_pTableView->openPersistentEditor(index); | ||||||||||
m_persistentEditorState = PersistentEditor_Open; | ||||||||||
m_currentEditedCellIndex = index; | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::closeCurrentPersistentRatingEditor(bool rememberForRestore) { | ||||||||||
if (m_persistentEditorState == PersistentEditor_Open) { | ||||||||||
m_pTableView->closePersistentEditor(m_currentEditedCellIndex); | ||||||||||
m_currentEditedCellIndex = QModelIndex(); | ||||||||||
} | ||||||||||
|
||||||||||
if (rememberForRestore && | ||||||||||
(m_persistentEditorState == PersistentEditor_Open || | ||||||||||
m_persistentEditorState == PersistentEditor_ShouldRestore)) { | ||||||||||
// Keep m_currentEditedCellIndex so the persistent editor | ||||||||||
// can be restored when the currently active explicit editor | ||||||||||
// is closed. | ||||||||||
m_persistentEditorState = PersistentEditor_ShouldRestore; | ||||||||||
} else { | ||||||||||
m_persistentEditorState = PersistentEditor_NotOpen; | ||||||||||
m_currentEditedCellIndex = QPersistentModelIndex(); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::restorePersistentRatingEditor(const QModelIndex& index) { | ||||||||||
if (m_persistentEditorState == PersistentEditor_ShouldRestore && | ||||||||||
index.isValid() && m_currentEditedCellIndex == index) { | ||||||||||
// Avoid race conditions by deferring the restore until | ||||||||||
// we have returned to the event loop, and the closing of | ||||||||||
// the current editor has been finished. Otherwise, | ||||||||||
// the internal state of QAbstractItemView may become | ||||||||||
// inconsistent. | ||||||||||
m_persistentEditorState = PersistentEditor_InDeferredRestore; | ||||||||||
QTimer::singleShot(0, this, &StarDelegate::restorePersistentRatingEditorNow); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
void StarDelegate::restorePersistentRatingEditorNow() { | ||||||||||
if (m_persistentEditorState == PersistentEditor_InDeferredRestore && | ||||||||||
m_currentEditedCellIndex.isValid()) { | ||||||||||
openPersistentRatingEditor(m_currentEditedCellIndex); | ||||||||||
} | ||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,7 @@ | ||||||||||||||||||||||||||||||
#pragma once | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
#include <QAbstractItemView> | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
#include "library/tabledelegates/tableitemdelegate.h" | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
class StarDelegate : public TableItemDelegate { | ||||||||||||||||||||||||||||||
|
@@ -31,9 +33,27 @@ class StarDelegate : public TableItemDelegate { | |||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private slots: | ||||||||||||||||||||||||||||||
void commitAndCloseEditor(); | ||||||||||||||||||||||||||||||
void closingEditor(QWidget* widget, QAbstractItemDelegate::EndEditHint hint); | ||||||||||||||||||||||||||||||
void cellEntered(const QModelIndex& index); | ||||||||||||||||||||||||||||||
void cursorNotOverAnyCell(); | ||||||||||||||||||||||||||||||
void editRequested(const QModelIndex& index, | ||||||||||||||||||||||||||||||
QAbstractItemView::EditTrigger trigger, | ||||||||||||||||||||||||||||||
QEvent* event); | ||||||||||||||||||||||||||||||
void restorePersistentRatingEditorNow(); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private: | ||||||||||||||||||||||||||||||
void openPersistentRatingEditor(const QModelIndex& index); | ||||||||||||||||||||||||||||||
void closeCurrentPersistentRatingEditor(bool rememberForRestore); | ||||||||||||||||||||||||||||||
void restorePersistentRatingEditor(const QModelIndex& index); | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
enum PersistentEditorState { | ||||||||||||||||||||||||||||||
PersistentEditor_NotOpen, | ||||||||||||||||||||||||||||||
PersistentEditor_Opening, | ||||||||||||||||||||||||||||||
PersistentEditor_Open, | ||||||||||||||||||||||||||||||
PersistentEditor_ShouldRestore, | ||||||||||||||||||||||||||||||
PersistentEditor_InDeferredRestore | ||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||
Comment on lines
+49
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We try not to use bare enums in new codebase, but instead scoped enum
Suggested change
This will allow you to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this code was written before I learned about the existence of |
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
QPersistentModelIndex m_currentEditedCellIndex; | ||||||||||||||||||||||||||||||
bool m_isOneCellInEditMode; | ||||||||||||||||||||||||||||||
PersistentEditorState m_persistentEditorState; | ||||||||||||||||||||||||||||||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.