diff --git a/src/skin/legacy/legacyskinparser.cpp b/src/skin/legacy/legacyskinparser.cpp index 8ac490ecdb3..85acbb08738 100644 --- a/src/skin/legacy/legacyskinparser.cpp +++ b/src/skin/legacy/legacyskinparser.cpp @@ -1176,13 +1176,15 @@ QWidget* LegacySkinParser::parseTrackProperty(const QDomElement& node) { return nullptr; } - bool isMainDeck = PlayerManager::isDeckGroup(group); + int mainDeckNum = 0; + bool isMainDeck = PlayerManager::isDeckGroup(group, &mainDeckNum); WTrackProperty* pTrackProperty = new WTrackProperty( m_pParent, m_pConfig, m_pLibrary, group, - isMainDeck); + isMainDeck, + mainDeckNum); setupLabelWidget(node, pTrackProperty); // Ensure 'show_track_menu' control is created for each main deck and diff --git a/src/widget/wtrackproperty.cpp b/src/widget/wtrackproperty.cpp index ca1f0d03ec3..d517b9eb094 100644 --- a/src/widget/wtrackproperty.cpp +++ b/src/widget/wtrackproperty.cpp @@ -23,12 +23,15 @@ WTrackProperty::WTrackProperty( UserSettingsPointer pConfig, Library* pLibrary, const QString& group, - bool isMainDeck) + bool isMainDeck, + int deckNum) : WLabel(pParent), m_group(group), m_pConfig(pConfig), m_pLibrary(pLibrary), m_isMainDeck(isMainDeck), + m_isComment(false), + m_mainDeckNum(deckNum), m_propertyIsWritable(false), m_pSelectedClickTimer(nullptr), m_bSelected(false), @@ -65,6 +68,34 @@ void WTrackProperty::setup(const QDomNode& node, const SkinContext& context) { return; } m_editProperty = m_displayProperty; + if (m_editProperty == QStringLiteral("comment")) { + m_isComment = true; + } + } + + // Create a QAction with a hotkey which allows us to open the editor without + // having to use the mouse + if (m_isComment && m_isMainDeck && m_mainDeckNum > 0) { + parented_ptr pEditCommentAction = make_parented("edit comment", this); + pEditCommentAction->setShortcut(QKeySequence(tr("Alt+%1").arg(m_mainDeckNum))); + connect(pEditCommentAction.get(), + &QAction::triggered, + this, + [this]() { + // Assumes only one comment label is visible. + // If there are more, Qt _should_ trigger the first QAction + // in the internal list and throw a warning for the others + // "QAction::event: Ambiguous shortcut overload: [shortcut]" + // Anyways, as soon as one editor is opened (gets focus), + // others will receive a focusOut event and close. + if (isVisible() && + (!m_pEditor || (m_pEditor && !m_pEditor->isVisible()))) { + // Note: if the editor is already visible, this would + // reload/reset the comment from the track + openEditor(); + } + }); + addAction(pEditCommentAction); } m_propertyIsWritable = true; } @@ -97,6 +128,10 @@ void WTrackProperty::slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldT void WTrackProperty::slotTrackChanged(TrackId trackId) { Q_UNUSED(trackId); updateLabel(); + if (m_pEditor && m_pEditor->isVisible()) { + // Close and discard new text + m_pEditor->hide(); + } } void WTrackProperty::updateLabel() { @@ -149,28 +184,7 @@ void WTrackProperty::mousePressEvent(QMouseEvent* pEvent) { this, &WTrackProperty::resetSelectedState); } else if (m_pSelectedClickTimer->isActive()) { resetSelectedState(); - // create the persistent editor, populate & connect - if (!m_pEditor) { - m_pEditor = make_parented(this); - connect(m_pEditor, - // use custom signal. editingFinished() doesn't suit since it's - // also emitted weh pressing Esc (which should cancel editing) - &WTrackPropertyEditor::commitEditorData, - this, - &WTrackProperty::slotCommitEditorData); - } - // Don't let the editor expand beyond its initial size - m_pEditor->setFixedSize(size()); - - QString editText = getPropertyStringFromTrack(m_editProperty); - if (m_displayProperty == "titleInfo" && editText.isEmpty()) { - editText = tr("title"); - } - m_pEditor->setText(editText); - m_pEditor->selectAll(); - m_pEditor->show(); - m_pEditor->setFocus(); - return; + openEditor(); } // start timer m_pSelectedClickTimer->start(); @@ -308,15 +322,68 @@ void WTrackProperty::slotShowTrackMenuChangeRequest(bool show) { contextMenuEvent(pEvent); } +void WTrackProperty::openEditor() { + resetSelectedState(); + if (!m_pCurrentTrack) { + return; + } + // create the persistent editor, populate & connect + if (!m_pEditor) { + m_pEditor = make_parented(this); + connect(m_pEditor, + // use custom signal. editingFinished() doesn't suit since it's + // also emitted weh pressing Esc (which should cancel editing) + &WTrackPropertyEditor::commitEditorData, + this, + &WTrackProperty::slotCommitEditorData); + } + // Don't let the editor expand beyond its initial size + m_pEditor->setFixedSize(size()); + + QString editText = getPropertyStringFromTrack(m_editProperty); + if (m_displayProperty == "titleInfo" && editText.isEmpty()) { + editText = tr("title"); + } + m_pEditor->setText(editText); + m_pEditor->selectAll(); + m_pEditor->show(); + m_pEditor->setFocus(); +} + void WTrackProperty::slotCommitEditorData(const QString& text) { + if (!m_pCurrentTrack) { + return; + } + // use real track data instead of text() to be independent from display text - if (m_pCurrentTrack && text != getPropertyStringFromTrack(m_editProperty)) { - const QVariant var(QVariant::fromValue(text)); - m_pCurrentTrack->setProperty( - m_editProperty.toUtf8().constData(), - var); - // Track::changed() will update label + const QString trackText = getPropertyStringFromTrack(m_editProperty); + QString editorText = text; + if (m_isComment) { + // Transform ALL occurrences of \n into linebreaks. + // Existing linebreaks are not affected. + QString cr(QChar::CarriageReturn); + cr.append(QChar::LineFeed); + editorText.replace("\\n", cr); + // For multi-line comments, the editor received only the first line. + // In order to keep the other lines, we need to replace + // the first line of the original text with the editor text. + // (which may add new linebreaks) + // Note: assumes the comment didn't change while we were editing it. + int firstLB = trackText.indexOf('\n'); + if (firstLB >= 0) { // has linebreak + QString trackTSliced = trackText; + trackTSliced = trackTSliced.sliced(firstLB); + editorText.append(trackTSliced); + } + } + if (editorText == trackText) { + return; } + const QVariant var(QVariant::fromValue(editorText)); + m_pCurrentTrack->setProperty( + m_editProperty.toUtf8().constData(), + var); + // Track::changed() will update label } void WTrackProperty::resetSelectedState() { diff --git a/src/widget/wtrackproperty.h b/src/widget/wtrackproperty.h index 0e97f5efe98..9fce54a3742 100644 --- a/src/widget/wtrackproperty.h +++ b/src/widget/wtrackproperty.h @@ -42,7 +42,8 @@ class WTrackProperty : public WLabel, public TrackDropTarget { UserSettingsPointer pConfig, Library* pLibrary, const QString& group, - bool isMainDeck); + bool isMainDeck, + int mainDeckNum); ~WTrackProperty() override; // Custom property to allow skins to style the 'selected' state when the // widget awaits a second click to open the editor. It's reset automatically @@ -89,10 +90,15 @@ class WTrackProperty : public WLabel, public TrackDropTarget { void restyleAndRepaint(); void ensureTrackMenuIsCreated(); + void openEditor(); + const QString m_group; const UserSettingsPointer m_pConfig; Library* m_pLibrary; const bool m_isMainDeck; + bool m_isComment; + int m_mainDeckNum; + TrackPointer m_pCurrentTrack; QString m_displayProperty;