From 25af3b4ae5b68c679dafc7a39df03a5cc6c4245b Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Wed, 20 May 2026 11:10:25 +0200 Subject: [PATCH 1/7] Add the ability to hide a track in pattern editor --- include/Clip.h | 2 ++ include/Track.h | 3 +++ include/TrackOperationsWidget.h | 1 + include/TrackView.h | 3 +++ src/core/Track.cpp | 17 +++++++++++++++++ src/gui/tracks/TrackOperationsWidget.cpp | 11 +++++++++++ src/gui/tracks/TrackView.cpp | 24 +++++++++++++++++++++++- 7 files changed, 60 insertions(+), 1 deletion(-) diff --git a/include/Clip.h b/include/Clip.h index e3c29138254..debbfd5b04c 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -49,6 +49,7 @@ class TrackView; class LMMS_EXPORT Clip : public Model, public JournallingObject { Q_OBJECT + mapPropertyFromModel(bool,isHidden,setHidden,m_hiddenModel); mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); public: @@ -171,6 +172,7 @@ public slots: TimePos m_length; TimePos m_startTimeOffset; + BoolModel m_hiddenModel; BoolModel m_mutedModel; BoolModel m_soloModel; bool m_autoResize = true; diff --git a/include/Track.h b/include/Track.h index 92cb38f26e6..75e444b7ea7 100644 --- a/include/Track.h +++ b/include/Track.h @@ -138,6 +138,8 @@ class LMMS_EXPORT Track : public Model, public JournallingObject void createClipsForPattern(int pattern); + bool hiddenForPattern(int pattern); + void setVisibilityForPattern(bool hidden, int pattern); void insertBar( const TimePos & pos ); void removeBar( const TimePos & pos ); @@ -238,6 +240,7 @@ public slots: void nameChanged(); void clipAdded( lmms::Clip * ); void colorChanged(); + void visibilityChanged(); } ; diff --git a/include/TrackOperationsWidget.h b/include/TrackOperationsWidget.h index ed3b72c81b2..ed9500102a4 100644 --- a/include/TrackOperationsWidget.h +++ b/include/TrackOperationsWidget.h @@ -63,6 +63,7 @@ private slots: void recordingOn(); void recordingOff(); void clearTrack(); + void hideTrack(); private: TrackView * m_trackView; diff --git a/include/TrackView.h b/include/TrackView.h index 4954071927a..0ab71e99dde 100644 --- a/include/TrackView.h +++ b/include/TrackView.h @@ -104,9 +104,12 @@ class TrackView : public QWidget, public ModelView, public JournallingObject // Currently instrument track and sample track supports it virtual QMenu * createMixerMenu(QString title, QString newMixerLabel); + void setVisibleForThisPattern(bool hidden); + public slots: virtual bool close(); + void visibilityChanged(); protected: diff --git a/src/core/Track.cpp b/src/core/Track.cpp index e44475d9374..01d5732197f 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -505,6 +505,23 @@ void Track::createClipsForPattern(int pattern) } +bool Track::hiddenForPattern(int pattern) +{ + if (pattern >= m_clips.size()) + return false; + return m_clips[pattern]->isHidden(); +} + +void Track::setVisibilityForPattern(bool hidden, int pattern) +{ + if (pattern < m_clips.size()) + { + m_clips[pattern]->setHidden(hidden); + emit visibilityChanged(); + } +} + + /*! \brief Move all the clips after a certain time later by one bar. diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index 16c0ca3e424..adad435b79c 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -43,6 +43,7 @@ #include "InstrumentTrackView.h" #include "lmms_math.h" #include "KeyboardShortcuts.h" +#include "PatternStore.h" #include "Song.h" #include "StringPairDrag.h" #include "Track.h" @@ -236,6 +237,12 @@ void TrackOperationsWidget::clearTrack() } +void TrackOperationsWidget::hideTrack() +{ + m_trackView->setVisibleForThisPattern(true); +} + + /*! \brief Remove this track from the track list * */ @@ -313,6 +320,10 @@ void TrackOperationsWidget::updateMenu() { toMenu->addAction( tr( "Clear this track" ), this, SLOT(clearTrack())); } + if (m_trackView->getTrack()->trackContainer() == Engine::patternStore()) + { + toMenu->addAction(tr( "Hide this track for current pattern" ), this, SLOT(hideTrack())); + } if (QMenu *mixerMenu = m_trackView->createMixerMenu(tr("Channel %1: %2"), tr("Assign to new Mixer Channel"))) { toMenu->addMenu(mixerMenu); diff --git a/src/gui/tracks/TrackView.cpp b/src/gui/tracks/TrackView.cpp index 1d282338f6f..42808c2ab94 100644 --- a/src/gui/tracks/TrackView.cpp +++ b/src/gui/tracks/TrackView.cpp @@ -39,6 +39,7 @@ #include "DeprecationHelper.h" #include "Engine.h" #include "FadeButton.h" +#include "PatternStore.h" #include "StringPairDrag.h" #include "Track.h" #include "TrackGrip.h" @@ -95,6 +96,8 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : this, SLOT(createClipView(lmms::Clip*)), Qt::QueuedConnection ); + connect(m_track, SIGNAL(visibilityChanged()), this, SLOT(visibilityChanged())); + connect( &m_track->m_mutedModel, SIGNAL(dataChanged()), &m_trackContentWidget, SLOT(update())); @@ -115,6 +118,7 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : } m_trackContainerView->addTrackView( this ); + visibilityChanged(); } @@ -157,7 +161,7 @@ void TrackView::update() { m_trackContentWidget.changePosition(); } - QWidget::update(); + visibilityChanged(); } @@ -435,6 +439,13 @@ void TrackView::createClipView( Clip * clip ) +void TrackView::setVisibleForThisPattern(bool hidden) +{ + m_track->setVisibilityForPattern(hidden, Engine::patternStore()->currentPattern()); +} + + + void TrackView::muteChanged() { @@ -443,6 +454,17 @@ void TrackView::muteChanged() } +void TrackView::visibilityChanged() +{ + PatternStore* ps = Engine::patternStore(); + if (m_track->trackContainer() == ps) + { + setVisible(!m_track->hiddenForPattern(ps->currentPattern())); + } + QWidget::update(); +} + + void TrackView::onTrackGripGrabbed() { m_action = Action::Move; From 52de624178a5f004efa31ca37aa6c80c9fd32f4a Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Wed, 20 May 2026 11:36:49 +0200 Subject: [PATCH 2/7] Add a button in the pattern editor to show all hidden tracks --- include/PatternEditor.h | 1 + src/gui/editors/PatternEditor.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/PatternEditor.h b/include/PatternEditor.h index 0ea0061203a..5220e49b584 100644 --- a/include/PatternEditor.h +++ b/include/PatternEditor.h @@ -68,6 +68,7 @@ public slots: protected slots: void dropEvent(QDropEvent * de ) override; void resizeEvent(QResizeEvent* de) override; + void showAllTracks(); void updatePosition(); void updatePixelsPerBar(); diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 6c3b66e29b5..846f8efff8d 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -191,6 +191,15 @@ void PatternEditor::resizeEvent(QResizeEvent* re) } +void PatternEditor::showAllTracks() +{ + for (const auto& trackView : trackViews()) + { + trackView->setVisibleForThisPattern(false); + } +} + + void PatternEditor::updatePosition() { //realignTracks(); @@ -322,6 +331,8 @@ PatternEditorWindow::PatternEditorWindow(PatternStore* ps) : m_editor, SLOT(addSampleTrack())); trackAndStepActionsToolBar->addAction(embed::getIconPixmap("add_automation"), tr("Add automation-track"), m_editor, SLOT(addAutomationTrack())); + trackAndStepActionsToolBar->addAction(embed::getIconPixmap(""), tr("Show all"), + m_editor, SLOT(showAllTracks())); auto stretch = new QWidget(m_toolBar); stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); From 14fe560210cf5a44125d10d70b8a1e1c576c026b Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Wed, 20 May 2026 13:22:37 +0200 Subject: [PATCH 3/7] Add virtual method isEmpty to Clip --- include/AutomationClip.h | 5 +++++ include/Clip.h | 5 +++++ include/MidiClip.h | 5 +++++ include/SampleClip.h | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/include/AutomationClip.h b/include/AutomationClip.h index f59917eaffc..ae511f08401 100644 --- a/include/AutomationClip.h +++ b/include/AutomationClip.h @@ -103,6 +103,11 @@ class LMMS_EXPORT AutomationClip : public Clip const bool ignoreSurroundingPoints = true ); + bool isEmpty() override + { + return m_timeMap.empty(); + } + void removeNode(const TimePos & time); void removeNodes(const int tick0, const int tick1); diff --git a/include/Clip.h b/include/Clip.h index debbfd5b04c..27b191f7c74 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -99,6 +99,11 @@ class LMMS_EXPORT Clip : public Model, public JournallingObject bool isInPattern() const; + virtual bool isEmpty() + { + return false; + } + bool manuallyResizable() const; /*! \brief Set whether a clip has been resized yet by the user or the knife tool. diff --git a/include/MidiClip.h b/include/MidiClip.h index d8553e87eab..8d232e32429 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -93,6 +93,11 @@ class LMMS_EXPORT MidiClip : public Clip return m_clipType; } + bool isEmpty() override + { + return !m_notes.size(); + } + // next/previous track based on position in the containing track MidiClip * previousMidiClip() const; diff --git a/include/SampleClip.h b/include/SampleClip.h index ea7853386e6..64f3f57c79f 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -79,6 +79,11 @@ class SampleClip : public Clip void setIsPlaying(bool isPlaying); void setSampleBuffer(std::shared_ptr sb); + bool isEmpty() override + { + return !m_sample.sampleSize(); + } + SampleClip* clone() override { return new SampleClip(*this); From f607d35de629e2445ecc9f3413f80aeca508e35d Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Wed, 20 May 2026 13:23:14 +0200 Subject: [PATCH 4/7] Add a button in the pattern editor to hide empty tracks --- include/PatternEditor.h | 1 + include/Track.h | 1 + include/TrackView.h | 1 + src/core/Track.cpp | 11 +++++++++++ src/gui/editors/PatternEditor.cpp | 10 ++++++++++ src/gui/tracks/TrackView.cpp | 5 ++++- 6 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/PatternEditor.h b/include/PatternEditor.h index 5220e49b584..526601973a5 100644 --- a/include/PatternEditor.h +++ b/include/PatternEditor.h @@ -69,6 +69,7 @@ protected slots: void dropEvent(QDropEvent * de ) override; void resizeEvent(QResizeEvent* de) override; void showAllTracks(); + void hideEmptyTracks(); void updatePosition(); void updatePixelsPerBar(); diff --git a/include/Track.h b/include/Track.h index 75e444b7ea7..08d014409b3 100644 --- a/include/Track.h +++ b/include/Track.h @@ -140,6 +140,7 @@ class LMMS_EXPORT Track : public Model, public JournallingObject bool hiddenForPattern(int pattern); void setVisibilityForPattern(bool hidden, int pattern); + void hideForPatternIfEmpty(int pattern); void insertBar( const TimePos & pos ); void removeBar( const TimePos & pos ); diff --git a/include/TrackView.h b/include/TrackView.h index 0ab71e99dde..2113bd13f1e 100644 --- a/include/TrackView.h +++ b/include/TrackView.h @@ -105,6 +105,7 @@ class TrackView : public QWidget, public ModelView, public JournallingObject virtual QMenu * createMixerMenu(QString title, QString newMixerLabel); void setVisibleForThisPattern(bool hidden); + void hideIfEmpty(); public slots: diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 01d5732197f..704aa1809a6 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -521,6 +521,17 @@ void Track::setVisibilityForPattern(bool hidden, int pattern) } } +void Track::hideForPatternIfEmpty(int pattern) +{ + if (pattern < m_clips.size()) + { + if (m_clips[pattern]->isEmpty()) + { + setVisibilityForPattern(true, pattern); + } + } +} + diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 846f8efff8d..24f51065f14 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -199,6 +199,14 @@ void PatternEditor::showAllTracks() } } +void PatternEditor::hideEmptyTracks() +{ + for (const auto& trackView : trackViews()) + { + trackView->hideIfEmpty(); + } +} + void PatternEditor::updatePosition() { @@ -333,6 +341,8 @@ PatternEditorWindow::PatternEditorWindow(PatternStore* ps) : m_editor, SLOT(addAutomationTrack())); trackAndStepActionsToolBar->addAction(embed::getIconPixmap(""), tr("Show all"), m_editor, SLOT(showAllTracks())); + trackAndStepActionsToolBar->addAction(embed::getIconPixmap(""), tr("Hide empty"), + m_editor, SLOT(hideEmptyTracks())); auto stretch = new QWidget(m_toolBar); stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); diff --git a/src/gui/tracks/TrackView.cpp b/src/gui/tracks/TrackView.cpp index 42808c2ab94..34e68204e00 100644 --- a/src/gui/tracks/TrackView.cpp +++ b/src/gui/tracks/TrackView.cpp @@ -443,7 +443,10 @@ void TrackView::setVisibleForThisPattern(bool hidden) { m_track->setVisibilityForPattern(hidden, Engine::patternStore()->currentPattern()); } - +void TrackView::hideIfEmpty() +{ + m_track->hideForPatternIfEmpty(Engine::patternStore()->currentPattern()); +} From 92cda22c88f77b633492ec37f7e79c2490999f05 Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Wed, 20 May 2026 14:06:08 +0200 Subject: [PATCH 5/7] update Clip serialisation --- src/core/AutomationClip.cpp | 2 ++ src/core/SampleClip.cpp | 2 ++ src/tracks/MidiClip.cpp | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index 32739482b74..22b1cb920da 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -785,6 +785,7 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "name", name() ); _this.setAttribute( "prog", QString::number( static_cast(progressionType()) ) ); _this.setAttribute( "tens", QString::number( getTension() ) ); + _this.setAttribute( "hidden", QString::number( isHidden() ) ); _this.setAttribute( "mute", QString::number( isMuted() ) ); _this.setAttribute("off", startTimeOffset()); _this.setAttribute("autoresize", QString::number(getAutoResize())); @@ -837,6 +838,7 @@ void AutomationClip::loadSettings( const QDomElement & _this ) setProgressionType( static_cast( _this.attribute( "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); + setHidden(_this.attribute( "hidden", QString::number( false ) ).toInt() ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); setAutoResize(_this.attribute("autoresize", "1").toInt()); setStartTimeOffset(_this.attribute("off").toInt()); diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index e279f76236e..b7dcaf8b573 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -274,6 +274,7 @@ void SampleClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "pos", startPosition() ); } _this.setAttribute( "len", length() ); + _this.setAttribute( "hidden", isHidden() ); _this.setAttribute( "muted", isMuted() ); _this.setAttribute( "src", sampleFile() ); _this.setAttribute( "off", startTimeOffset() ); @@ -324,6 +325,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) m_sample = Sample(std::move(buffer)); } changeLength( _this.attribute( "len" ).toInt() ); + setHidden( _this.attribute( "hidden" ).toInt() ); setMuted( _this.attribute( "muted" ).toInt() ); setStartTimeOffset( _this.attribute( "off" ).toInt() ); setAutoResize(_this.attribute("autoresize", "1").toInt()); diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index f1909f04b2d..dc392ff7b79 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -451,6 +451,7 @@ void MidiClip::exportToXML(QDomDocument& doc, QDomElement& midiClipElement, bool { midiClipElement.setAttribute("pos", startPosition()); } + midiClipElement.setAttribute("hidden", isHidden()); midiClipElement.setAttribute("muted", isMuted()); midiClipElement.setAttribute("steps", m_steps); midiClipElement.setAttribute("len", length()); @@ -489,6 +490,8 @@ void MidiClip::loadSettings( const QDomElement & _this ) { movePosition( _this.attribute( "pos" ).toInt() ); } + setHidden(static_cast(_this.attribute("hidden").toInt())); + if (static_cast(_this.attribute("muted").toInt()) != isMuted()) { toggleMute(); From 748935518f476d34dd03f54a9d629bb832b2ba8b Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Thu, 21 May 2026 09:43:48 +0200 Subject: [PATCH 6/7] fix warnings about comparison of signed and unsigned integers --- include/Track.h | 6 +++--- src/core/Track.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/Track.h b/include/Track.h index 08d014409b3..972a12d0c5f 100644 --- a/include/Track.h +++ b/include/Track.h @@ -138,9 +138,9 @@ class LMMS_EXPORT Track : public Model, public JournallingObject void createClipsForPattern(int pattern); - bool hiddenForPattern(int pattern); - void setVisibilityForPattern(bool hidden, int pattern); - void hideForPatternIfEmpty(int pattern); + bool hiddenForPattern(size_t pattern); + void setVisibilityForPattern(bool hidden, size_t pattern); + void hideForPatternIfEmpty(size_t pattern); void insertBar( const TimePos & pos ); void removeBar( const TimePos & pos ); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 704aa1809a6..9dd8670ca35 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -505,14 +505,14 @@ void Track::createClipsForPattern(int pattern) } -bool Track::hiddenForPattern(int pattern) +bool Track::hiddenForPattern(size_t pattern) { if (pattern >= m_clips.size()) return false; return m_clips[pattern]->isHidden(); } -void Track::setVisibilityForPattern(bool hidden, int pattern) +void Track::setVisibilityForPattern(bool hidden, size_t pattern) { if (pattern < m_clips.size()) { @@ -521,7 +521,7 @@ void Track::setVisibilityForPattern(bool hidden, int pattern) } } -void Track::hideForPatternIfEmpty(int pattern) +void Track::hideForPatternIfEmpty(size_t pattern) { if (pattern < m_clips.size()) { From 702080f3600201464db31a29c9b8913c8d5b8eb0 Mon Sep 17 00:00:00 2001 From: Louis BOSSY Date: Thu, 21 May 2026 09:48:38 +0200 Subject: [PATCH 7/7] update track view after undo/redo --- src/core/Clip.cpp | 4 ++++ src/core/Track.cpp | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Clip.cpp b/src/core/Clip.cpp index 3e769f4ea13..ed2324809f3 100644 --- a/src/core/Clip.cpp +++ b/src/core/Clip.cpp @@ -55,6 +55,8 @@ Clip::Clip( Track * track ) : if( getTrack() ) { getTrack()->addClip( this ); + connect(&m_hiddenModel, SIGNAL(dataChanged()), + getTrack(), SIGNAL(visibilityChanged())); } setJournalling( false ); movePosition( 0 ); @@ -84,6 +86,8 @@ Clip::Clip(const Clip& other): if (getTrack()) { getTrack()->addClip(this); + connect(&m_hiddenModel, SIGNAL(dataChanged()), + getTrack(), SIGNAL(visibilityChanged())); } } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 9dd8670ca35..3ae38b4172c 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -517,7 +517,6 @@ void Track::setVisibilityForPattern(bool hidden, size_t pattern) if (pattern < m_clips.size()) { m_clips[pattern]->setHidden(hidden); - emit visibilityChanged(); } } @@ -527,7 +526,7 @@ void Track::hideForPatternIfEmpty(size_t pattern) { if (m_clips[pattern]->isEmpty()) { - setVisibilityForPattern(true, pattern); + m_clips[pattern]->setHidden(true); } } }