diff --git a/include/SampleClip.h b/include/SampleClip.h index 5dd0f6b2ab8..b3fb249214a 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -26,6 +26,7 @@ #define LMMS_SAMPLE_CLIP_H #include + #include "Clip.h" #include "Sample.h" @@ -85,6 +86,9 @@ class SampleClip : public Clip void setIsPlaying(bool isPlaying); void setSampleBuffer(std::shared_ptr sb); + QString getName() const; + const QString& getNameRef() const; + SampleClip* clone() override { return new SampleClip(*this); @@ -107,8 +111,13 @@ public slots: bool m_isPlaying; int m_startFrameOffset; + QString m_name; //!< Display name + bool m_name_set = false; //!< Whether m_name was set by the user + friend class gui::SampleClipView; + void setName(const QString& name); + signals: void sampleChanged(); diff --git a/include/SampleClipView.h b/include/SampleClipView.h index 3fd9960495f..2dfab6d7186 100644 --- a/include/SampleClipView.h +++ b/include/SampleClipView.h @@ -27,6 +27,9 @@ #include "ClipView.h" +class QGraphicsView; +class QLineEdit; + namespace lmms { @@ -44,10 +47,13 @@ class SampleClipView : public ClipView SampleClipView( SampleClip * _clip, TrackView * _tv ); ~SampleClipView() override = default; + void keyPressEvent(QKeyEvent* ke) override; + public slots: void updateSample(); void reverseSample(); void setAutomationGhost(); + void renameClip(); @@ -59,12 +65,19 @@ public slots: void dropEvent( QDropEvent * _de ) override; void mouseDoubleClickEvent( QMouseEvent * ) override; void paintEvent( QPaintEvent * ) override; + void renameFinished(); private: SampleClip * m_clip; QPixmap m_paintPixmap; long m_paintPixmapXPosition; + + QGraphicsView* m_renameLineEditView; + QLineEdit* m_renameLineEdit; + bool m_inRename = false; + bool m_renameCancelled = false; // Whether the user escaped the renaming QLineEdit + int m_renameLineEditMaxWidth = 100; // Used to set the maximum width for the renaming QLineEdit } ; diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index d15ee6b07ed..dd823a629f0 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -84,6 +84,8 @@ SampleClip::SampleClip(const SampleClip& orig) : setSampleFile( "" ); restoreJournallingState(); + if (orig.m_name_set) { setName(orig.getName()); } + // we need to receive bpm-change-events, because then we have to // change length of this Clip connect(Engine::getSong(), &Song::tempoChanged, this, &SampleClip::tempoChanged, Qt::DirectConnection); @@ -154,6 +156,16 @@ void SampleClip::setSampleBuffer(std::shared_ptr sb) Engine::getSong()->setModified(); } +QString SampleClip::getName() const +{ + return m_name; +} + +const QString& SampleClip::getNameRef() const +{ + return m_name; +} + void SampleClip::setSampleFile(const QString& sf) { if (sf.isEmpty()) @@ -183,6 +195,12 @@ void SampleClip::setSampleFile(const QString& sf) updateLength(); } + if (!m_name_set) + { + // If file changed but name already modified, keep old name. (Prefer user-set over filename) + m_name = PathUtil::cleanName(m_sample.sampleFile()); + } + emit sampleChanged(); emit playbackPositionChanged(); } @@ -324,6 +342,11 @@ void SampleClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute("reversed", "true"); } // TODO: start- and end-frame + + if (m_name_set) // Only if it's changed from the filename + { + _this.setAttribute("name", m_name); + } } @@ -367,6 +390,11 @@ void SampleClip::loadSettings( const QDomElement & _this ) m_sample.setReversed(true); emit wasReversed(); // tell SampleClipView to update the view } + + if (_this.hasAttribute("name")) + { + setName(_this.attribute("name")); + } } @@ -378,4 +406,13 @@ gui::ClipView * SampleClip::createView( gui::TrackView * _tv ) } + + +void SampleClip::setName(const QString& name) +{ + m_name = name; + m_name_set = true; +} + + } // namespace lmms diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index e510901a119..7474043f082 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -27,11 +27,15 @@ #include #include #include +#include +#include +#include #include "FileDialog.h" #include "GuiApplication.h" #include "AutomationEditor.h" #include "embed.h" +#include "FontHelper.h" #include "PathUtil.h" #include "SampleClip.h" #include "SampleThumbnailRenderer.h" @@ -50,9 +54,40 @@ SampleClipView::SampleClipView( SampleClip * _clip, TrackView * _tv ) : m_paintPixmap(), m_paintPixmapXPosition(0) { + auto renameLineEditScene = new QGraphicsScene{}; + m_renameLineEdit = new QLineEdit{m_clip->getName(), nullptr}; + // update UI and tooltip updateSample(); + // Initialize renaming-related data + { + m_renameLineEdit->setFont(adjustedToPixelSize(font(), DEFAULT_FONT_SIZE)); + m_renameLineEdit->setReadOnly(true); + m_renameLineEdit->installEventFilter(this); + + m_renameLineEditView = new QGraphicsView{}; + m_renameLineEditView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_renameLineEditView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_renameLineEditView->setAttribute(Qt::WA_TransparentForMouseEvents, true); + m_renameLineEditView->setScene(renameLineEditScene); + + m_renameLineEditView->setStyleSheet("background: transparent; border: 0px;"); + + renameLineEditScene->addWidget(m_renameLineEdit); + + auto mainLayout = new QVBoxLayout{this}; + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + + mainLayout->addWidget(m_renameLineEditView, 0, Qt::AlignTop | Qt::AlignLeft); + + m_renameLineEditView->hide(); + + connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &SampleClipView::renameFinished); + } + + // track future changes of SampleClip connect(m_clip, SIGNAL(sampleChanged()), this, SLOT(updateSample())); @@ -61,6 +96,26 @@ SampleClipView::SampleClipView( SampleClip * _clip, TrackView * _tv ) : setStyle( QApplication::style() ); } +void SampleClipView::keyPressEvent(QKeyEvent* ke) +{ + // Only care about key inputs if in rename process + if (!m_inRename) { return; } + + if (ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return) + { + renameFinished(); + } + else if (ke->key() == Qt::Key_Escape) + { + m_renameCancelled = true; + renameFinished(); + } + else + { + ke->ignore(); + } +} + void SampleClipView::updateSample() { update(); @@ -99,6 +154,12 @@ void SampleClipView::constructContextMenu(QMenu* cm) SLOT(setAutomationGhost()) ); + auto renameAction = cm->addAction( + tr("Rename clip"), + this, + SLOT(renameClip()) + ); + renameAction->setEnabled(m_clip->sampleLength() != 0); } @@ -296,8 +357,14 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) SampleThumbnailRenderer::visualize(param, p); } - QString name = PathUtil::cleanName(m_clip->m_sample.sampleFile()); - paintTextLabel(name, p); + if (!m_inRename) // Otherwise replaced by renaming QLineEdit + { + paintTextLabel(m_clip->getNameRef(), p); + } + else + { + m_renameLineEdit->setFixedWidth(std::min(width(), m_renameLineEditMaxWidth)); + } // disable antialiasing for borders, since its not needed p.setRenderHint( QPainter::Antialiasing, false ); @@ -370,4 +437,59 @@ void SampleClipView::setAutomationGhost() aEditor->setFocus(); } + + +void SampleClipView::renameClip() +{ + m_inRename = true; + m_renameLineEdit->setReadOnly(false); + + m_renameLineEdit->setText(m_clip->getNameRef()); + + QFontMetrics fontMetrics(font()); + // Multiply width by 1.2 to give some breathing room + m_renameLineEditMaxWidth = fontMetrics.horizontalAdvance(m_clip->getNameRef()) * 1.2; + + m_renameLineEdit->setFixedWidth(m_renameLineEditMaxWidth); + m_renameLineEditView->setFixedSize(m_renameLineEdit->width(), m_renameLineEdit->height()); + + update(); + + m_renameLineEditView->show(); + + m_renameLineEditView->setFocus(); + m_renameLineEdit->selectAll(); + m_renameLineEdit->setFocus(); +} + + + +void SampleClipView::renameFinished() +{ + m_inRename = false; + + m_renameLineEdit->deselect(); + m_renameLineEdit->setReadOnly(true); + m_renameLineEdit->setFixedWidth(m_renameLineEdit->width()); + + if (!m_renameCancelled) + { + auto newName = m_renameLineEdit->text(); + + if (!newName.isEmpty() && m_clip->getNameRef() != newName) + { + m_clip->setName(newName); + m_renameLineEdit->setText(newName); + Engine::getSong()->setModified(); + } + } + + setFocus(); + m_renameLineEditView->hide(); + + m_renameCancelled = false; + + update(); +} + } // namespace lmms::gui