diff --git a/src/ClientData.h b/src/ClientData.h
index 6f50d8bc3..56277d958 100644
--- a/src/ClientData.h
+++ b/src/ClientData.h
@@ -344,6 +344,40 @@ protected:
       }
    }
 
+   // \brief Invoke predicate on the ClientData objects that have been created in
+   // this, but do not cause the creation of any.  Stop at the first for which
+   // the predicate returns true, and return a pointer to the corresponding
+   // object, or return nullptr if no return values were true.
+   // Beware that the sequence of visitation is not specified.
+   template< typename Function >
+   ClientData *FindIf( const Function &function )
+   {
+      auto data = GetData();
+      for( auto &pObject : data.mObject ) {
+         const auto &ptr = Dereferenceable(pObject);
+         if ( ptr && function ( *ptr ) )
+            return &*ptr;
+      }
+      return nullptr;
+   }
+
+   // const counterpart of previous, only compiles with a function that takes
+   // a value or const reference argument
+   template< typename Function >
+   const ClientData *FindIf( const Function &function ) const
+   {
+      auto data = GetData();
+      for( auto &pObject : data.mObject ) {
+         const auto &ptr = Dereferenceable(pObject);
+         if ( ptr ) {
+            const auto &c_ref = *ptr;
+            if ( function( c_ref ) );
+               return &*c_ref;
+         }
+      }
+      return nullptr;
+   }
+
    // \brief For each registered factory, if the corresponding object in this
    // is absent, then invoke the factory and store the result.
    void BuildAll()
diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp
index 1e125e63b..1304259ac 100644
--- a/src/TrackPanel.cpp
+++ b/src/TrackPanel.cpp
@@ -923,6 +923,7 @@ void TrackPanel::UpdateTrackVRuler(Track *t)
    for (auto channel : TrackList::Channels(t)) {
       auto &view = TrackView::Get( *channel );
       const auto height = view.GetHeight() - (kTopMargin + kBottomMargin);
+      rect.SetHeight( height );
       const auto subViews = view.GetSubViews( rect );
       if (subViews.empty())
          continue;
@@ -1142,7 +1143,7 @@ struct ChannelGroup final : TrackPanelGroup {
          auto &view = TrackView::Get( *channel );
          auto height = view.GetHeight();
          rect.SetTop( yy );
-         rect.SetHeight( height );
+         rect.SetHeight( height - kSeparatorThickness );
          const auto subViews = TrackView::Get( *channel ).GetSubViews( rect );
          auto y1 = yy;
          for ( const auto &subView : subViews ) {
diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp
index 7a3690bd6..2405cd382 100644
--- a/src/effects/nyquist/Nyquist.cpp
+++ b/src/effects/nyquist/Nyquist.cpp
@@ -1073,19 +1073,26 @@ bool NyquistEffect::ProcessOne()
             type = wxT("wave");
             spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL");
             // To do: accommodate split views
-            auto viewType = WaveTrackViewConstants::NoDisplay;
             auto displays = WaveTrackView::Get( *wt ).GetDisplays();
-            if (!displays.empty())
-               viewType = displays[0];
-            switch ( viewType )
-            {
-            case Waveform:
-               view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
-               break;
-            case Spectrum:
-               view = wxT("\"Spectrogram\"");
-               break;
-            default: view = wxT("NIL"); break;
+            auto format = [&]( decltype(displays[0]) display ){
+               switch ( display )
+               {
+               case Waveform:
+                  return (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
+               case Spectrum:
+                  return wxT("\"Spectrogram\"");
+               default: return wxT("NIL");
+               }
+            };
+            if (displays.empty())
+               view = wxT("NIL");
+            else if (displays.size() == 1)
+               view = format( displays[0] );
+            else {
+               view = wxT("(list");
+               for ( auto display : displays )
+                  view += wxString(wxT(" ")) + format( display );
+               view += wxT(")");
             }
          },
 #if defined(USE_MIDI)
diff --git a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp
index 43449dda8..56491c76e 100644
--- a/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp
+++ b/src/tracks/playabletrack/wavetrack/ui/SpectrumView.cpp
@@ -649,6 +649,6 @@ void SpectrumView::Draw(
 
 static const WaveTrackSubViews::RegisteredFactory key{
    []( WaveTrackView &view ){
-      return std::make_shared< SpectrumView >( view.FindTrack() );
+      return std::make_shared< SpectrumView >( view );
    }
 };
diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp
index 4c2e9e3f7..ebd597d6d 100644
--- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp
+++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackControls.cpp
@@ -690,10 +690,13 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu, void *pUserData)
 BEGIN_POPUP_MENU(WaveTrackMenuTable)
    POPUP_MENU_SEPARATOR()
 
-   // These radio items may become non-exclusive check items
-   POPUP_MENU_RADIO_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay)
-   POPUP_MENU_RADIO_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay)
-   POPUP_MENU_RADIO_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay)
+   // View types are now a non-exclusive choice.  The first two are mutually
+   // exclusive, but the view may be in a state with either of those, and also
+   // spectrogram, after a mouse drag.  Clicking any of these three makes that
+   // view take up all the height.
+   POPUP_MENU_CHECK_ITEM(OnWaveformID, _("Wa&veform"), OnSetDisplay)
+   POPUP_MENU_CHECK_ITEM(OnWaveformDBID, _("&Waveform (dB)"), OnSetDisplay)
+   POPUP_MENU_CHECK_ITEM(OnSpectrumID, _("&Spectrogram"), OnSetDisplay)
 
    POPUP_MENU_ITEM(OnSpectrogramSettingsID, _("S&pectrogram Settings..."), OnSpectrogramSettings)
    POPUP_MENU_SEPARATOR()
diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp
index 25949300a..69039b1be 100644
--- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp
+++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.cpp
@@ -12,11 +12,15 @@ Paul Licameli split from TrackPanel.cpp
 
 #include "../../../../Experimental.h"
 
+#include <numeric>
 #include <wx/graphics.h>
 
 #include "../../../../WaveClip.h"
 #include "../../../../WaveTrack.h"
 
+#include "../../../../HitTestResult.h"
+#include "../../../../ProjectHistory.h"
+#include "../../../../RefreshCode.h"
 #include "../../../../TrackArtist.h"
 #include "../../../../TrackPanelDrawingContext.h"
 #include "../../../../TrackPanelMouseEvent.h"
@@ -27,6 +31,335 @@ Paul Licameli split from TrackPanel.cpp
 
 #include "../../../ui/TimeShiftHandle.h"
 
+namespace {
+
+using WaveTrackSubViewPtrs = std::vector< std::shared_ptr< WaveTrackSubView > >;
+
+// Structure that collects and modifies information on sub-view positions
+// Written with great generality, allowing any number of sub-views
+struct SubViewAdjuster
+{
+   enum { HotZoneSize = 5 }; // so many pixels at top and bottom of each subview
+
+   SubViewAdjuster( WaveTrackView &view )
+      : mwView{
+         std::static_pointer_cast<WaveTrackView>( view.shared_from_this() ) }
+   {
+      mSubViews = view.GetAllSubViews();
+      mOrigPlacements = mNewPlacements = view.SavePlacements();
+      FindPermutation();
+   }
+
+   void FindPermutation()
+   {
+      // Find a certain sort of the sub-views
+      auto size = mOrigPlacements.size();
+      wxASSERT( mSubViews.size() == size );
+      mPermutation.resize( size );
+      const auto begin = mPermutation.begin(), end = mPermutation.end();
+      std::iota( begin, end, 0 );
+      static auto invisible = []( const WaveTrackSubViewPlacement &placement ){
+         return placement.index < 0 || placement.fraction <= 0;
+      };
+      const auto comp = [this]( size_t ii, size_t jj ){
+         auto &pi = mOrigPlacements[ii];
+         bool iInvisible = invisible( pi );
+
+         auto &pj = mOrigPlacements[jj];
+         bool jInvisible = invisible( pj );
+
+         // Sort the invisibles to the front, rest by index
+         if ( iInvisible != jInvisible )
+            return iInvisible;
+         else if ( !iInvisible )
+            return pi.index < pj.index;
+         else
+            // Minor sort among the invisible views by their type
+            return mSubViews[ii]->SubViewType() < mSubViews[jj]->SubViewType();
+      };
+      std::sort( begin, end, comp );
+      // Find the start of visible sub-views
+      auto first = std::find_if( begin, end, [this](size_t ii){
+         return !invisible( mOrigPlacements[ii] );
+      } );
+      mFirstSubView = first - begin;
+   }
+
+   bool ModifyPermutation( bool top )
+   {
+      bool rotated = false;
+      const auto pBegin = mPermutation.begin(), pEnd = mPermutation.end();
+      auto pFirst = pBegin + mFirstSubView;
+      if ( mFirstSubView > 0 ) {
+         // In case of dragging the top edge of the topmost view, or the
+         // bottom edge of the bottommost, decide which of the invisible
+         // views can become visible, and reassign the sequence.
+         // For definiteness, that choice depends on the subview type numbers;
+         // see the sorting criteria above.
+         --mFirstSubView;
+         --pFirst;
+         if ( top ) {
+            // If you drag down the top, the greatest-numbered invisible
+            // subview type will appear there.
+            mNewPlacements[ *pFirst ].fraction = 0;
+         }
+         else {
+            // If you drag up the bottom, let the least-numbered invisible
+            // subview type appear there.
+            mNewPlacements[ *pBegin ].fraction = 0;
+            std::rotate( pBegin, pBegin + 1, pEnd );
+            rotated = true;
+         }
+      }
+      // Reassign index numbers to all sub-views and 0 fraction to invisibles
+      for ( auto pIter = pBegin; pIter != pFirst; ++pIter ) {
+         auto &placement = mNewPlacements[ *pIter ];
+         placement.index = -1;
+         placement.fraction = 0;
+      }
+      size_t index = 0;
+      for ( auto pIter = pFirst; pIter != pEnd; ++pIter )
+         mNewPlacements[ *pIter ].index = index++;
+      return rotated;
+   }
+
+   std::pair< size_t, bool >
+   HitTest( WaveTrackSubView &subView,
+      wxCoord yy, wxCoord top, wxCoord height )
+   {
+      const auto begin = mPermutation.begin(), end = mPermutation.end();
+      auto iter = std::find_if( begin, end, [&](size_t ii){
+         return mSubViews[ ii ].get() == &subView;
+      } );
+      auto index = iter - begin;
+      auto size = mPermutation.size();
+      if ( index < size ) {
+         yy -= top;
+         if ( yy >= 0 && yy < HotZoneSize && index > 0 )
+            return { index, true }; // top hit
+         if ( yy < height && yy >= height - HotZoneSize &&
+            // Have not yet called ModifyPermutation; dragging bottom of
+            // bottommost view allowed only if at least one view is invisible
+            ( index < size - 1 || mFirstSubView > 0 ) )
+            return { index, false }; // bottom hit
+      }
+      return { size, false }; // not hit
+   }
+
+   void UpdateViews( bool rollback )
+   {
+      auto pView = mwView.lock();
+      if ( pView ) {
+         auto pTrack = static_cast< WaveTrack* >( pView->FindTrack().get() );
+         for ( auto pChannel : TrackList::Channels<WaveTrack>( pTrack ) )
+            WaveTrackView::Get( *pChannel ).RestorePlacements(
+               rollback ? mOrigPlacements : mNewPlacements );
+      }
+   }
+
+   std::weak_ptr< WaveTrackView > mwView;
+   WaveTrackSubViewPtrs mSubViews;
+   WaveTrackSubViewPlacements mOrigPlacements, mNewPlacements;
+   // Array mapping ordinal into the placement and subview arrays
+   std::vector< size_t > mPermutation;
+   // index into mPermutation
+   size_t mFirstSubView{};
+};
+
+class SubViewAdjustHandle : public UIHandle
+{
+public:
+   enum { MinHeight = SubViewAdjuster::HotZoneSize };
+
+   static UIHandlePtr HitTest(
+      WaveTrackView &view, WaveTrackSubView &subView,
+      const TrackPanelMouseState &state )
+   {
+      SubViewAdjuster adjuster{ view };
+      auto hit = adjuster.HitTest( subView,
+         state.state.GetY(), state.rect.GetTop(), state.rect.GetHeight() );
+      auto index = hit.first;
+
+      if ( index < adjuster.mPermutation.size() )
+         return std::make_shared< SubViewAdjustHandle >(
+            std::move( adjuster ), index, hit.second
+         );
+      else
+         return {};
+   }
+
+   SubViewAdjustHandle(
+      SubViewAdjuster &&adjuster, size_t subViewIndex, bool top )
+      : mAdjuster{ std::move( adjuster ) }
+      , mMySubView{ subViewIndex }
+      , mTop{ top }
+   {
+      if ( mAdjuster.ModifyPermutation( top ) )
+         --mMySubView;
+   }
+
+   Result Click(
+      const TrackPanelMouseEvent &event, AudacityProject * ) override
+   {
+      using namespace RefreshCode;
+      const auto &permutation = mAdjuster.mPermutation;
+      const auto size = permutation.size();
+      if ( mMySubView >= size )
+         return Cancelled;
+
+      const auto &rect = event.rect;
+      const auto height = rect.GetHeight();
+      mOrigHeight = height;
+
+      wxASSERT( height ==
+         mAdjuster.mOrigPlacements[ mAdjuster.mPermutation[ mMySubView ] ]
+            .fraction
+      );
+
+      // Find the total height of the sub-views that may resize
+      // Note that this depends on the redenomination of fractions that
+      // happened in the last call to GetSubViews
+      mTotalHeight = 0;
+      const auto begin = permutation.begin();
+      auto iter = begin + ( mTop ? mAdjuster.mFirstSubView : mMySubView );
+      const auto end = ( mTop ? begin + mMySubView + 1 : permutation.end() );
+      for (; iter != end; ++iter) {
+         const auto &placement = mAdjuster.mOrigPlacements[ *iter ];
+         mTotalHeight += placement.fraction;
+      }
+
+      // Compute the maximum and minimum Y coordinates for drag effect
+      if ( mTop ) {
+         mOrigY = rect.GetTop();
+         mYMax = rect.GetBottom();
+         mYMin = mYMax - mTotalHeight + 1;
+      }
+      else {
+         mOrigY = rect.GetBottom();
+         mYMin = rect.GetTop();
+         mYMax = mYMin + mTotalHeight - 1;
+      }
+
+      return RefreshNone;
+   }
+
+   Result Drag( const TrackPanelMouseEvent &event, AudacityProject * ) override
+   {
+      using namespace RefreshCode;
+      auto pView = mAdjuster.mwView.lock();
+      if ( !pView )
+         return Cancelled;
+
+      // Find new height for the dragged sub-view
+      auto newY = std::max( mYMin, std::min( mYMax, event.event.GetY() ) );
+      const auto delta = newY - mOrigY;
+      wxCoord newHeight = mTop
+         ? mOrigHeight - delta
+         : mOrigHeight + delta
+      ;
+      wxASSERT( newHeight >= 0 && newHeight <= mTotalHeight );
+      if ( newHeight < MinHeight )
+         // Snap the dragged sub-view to nothing
+         newHeight = 0;
+
+      // Reassign height for the dragged sub-view
+      auto &myPlacement =
+         mAdjuster.mNewPlacements[ mAdjuster.mPermutation[ mMySubView ] ];
+      myPlacement.fraction = newHeight;
+
+      // Grow or shrink other sub-views
+      auto excess = newHeight - mOrigHeight; // maybe negative
+      const auto adjustHeight = [&](size_t ii) {
+         if (excess == 0)
+            return true;
+
+         auto index = mAdjuster.mPermutation[ ii ];
+
+         const auto &origPlacement = mAdjuster.mOrigPlacements[ index ];
+         const auto oldFraction = origPlacement.fraction;
+
+         auto &placement = mAdjuster.mNewPlacements[ index ];
+         auto &fraction = placement.fraction;
+
+         if (excess > oldFraction) {
+            excess -= oldFraction, fraction = 0;
+            return false;
+         }
+         else {
+            auto newFraction = oldFraction - excess;
+            if ( newFraction < MinHeight ) {
+               // This snaps very short sub-views to nothing
+               myPlacement.fraction += newFraction;
+               fraction = 0;
+            }
+            else
+               fraction = newFraction;
+            return true;
+         }
+      };
+      if ( mTop ) {
+         for ( size_t ii = mMySubView; ii > 0; ) {
+            --ii;
+            if ( adjustHeight( ii ) )
+               break;
+         }
+      }
+      else {
+         for ( size_t ii = mMySubView + 1, size = mAdjuster.mPermutation.size();
+            ii < size; ++ii
+         ) {
+            if ( adjustHeight( ii ) )
+               break;
+         }
+      }
+
+      // Save adjustment to the track and request a redraw
+      mAdjuster.UpdateViews( false );
+      return RefreshAll;
+   }
+
+   HitTestPreview Preview(
+      const TrackPanelMouseState &state, const AudacityProject * ) override
+   {
+      static wxCursor resizeCursor{ wxCURSOR_SIZENS };
+      return {
+         _("Click and drag to adjust sizes of sub-views."),
+         &resizeCursor
+      };
+   }
+
+   Result Release(
+      const TrackPanelMouseEvent &event, AudacityProject *pProject,
+      wxWindow *pParent) override
+   {
+      ProjectHistory::Get( *pProject ).ModifyState( false );
+      return RefreshCode::RefreshNone;
+   }
+
+   Result Cancel( AudacityProject * ) override
+   {
+      mAdjuster.UpdateViews( true );
+      return RefreshCode::RefreshAll;
+   }
+
+private:
+
+   SubViewAdjuster mAdjuster;
+
+   // An index into mAdjuster.mPermutation
+   size_t mMySubView{};
+
+   wxCoord mYMin{}, mYMax{};
+   wxCoord mTotalHeight{};
+   wxCoord mOrigHeight{};
+   wxCoord mOrigY{};
+
+   // Whether we drag the top or the bottom of the sub-view
+   bool mTop{};
+};
+
+}
+
 std::pair<
    bool, // if true, hit-testing is finished
    std::vector<UIHandlePtr>
@@ -36,8 +369,20 @@ std::pair<
    const std::shared_ptr<WaveTrack> &wt,
    CommonTrackView &view)
 {
-   return WaveTrackView::DoDetailedHitTest(
-      state, pProject, currentTool, bMultiTool, wt, view);
+   auto results = WaveTrackView::DoDetailedHitTest(
+      state, pProject, currentTool, bMultiTool, wt, view );
+   if ( results.first )
+      return results;
+
+   auto pWaveTrackView = mwWaveTrackView.lock();
+   if ( pWaveTrackView && !state.state.HasModifiers() ) {
+      auto pHandle = SubViewAdjustHandle::HitTest(
+         *pWaveTrackView, *this, state );
+      if (pHandle)
+         results.second.push_back( pHandle );
+   }
+
+   return results;
 }
 
 WaveTrackView &WaveTrackView::Get( WaveTrack &track )
@@ -53,22 +398,13 @@ const WaveTrackView &WaveTrackView::Get( const WaveTrack &track )
 WaveTrackView::WaveTrackView( const std::shared_ptr<Track> &pTrack )
    : CommonTrackView{ pTrack }
 {
-   WaveTrackSubViews::BuildAll();
+}
 
-   auto display = TracksPrefs::ViewModeChoice();
-
-   // Force creation always:
-   WaveformSettings &settings = static_cast< WaveTrack* >( pTrack.get() )
-      ->GetIndependentWaveformSettings();
-
-   if (display == WaveTrackViewConstants::obsoleteWaveformDBDisplay) {
-      display = WaveTrackViewConstants::Waveform;
-      settings.scaleType = WaveformSettings::stLogarithmic;
-   }
-
-   mPlacements.resize( WaveTrackSubViews::size() );
-
-   SetDisplay( display );
+WaveTrackSubView::WaveTrackSubView( WaveTrackView &waveTrackView )
+   : CommonTrackView( waveTrackView.FindTrack() )
+{
+   mwWaveTrackView = std::static_pointer_cast<WaveTrackView>(
+      waveTrackView.shared_from_this() );
 }
 
 WaveTrackView::~WaveTrackView()
@@ -128,6 +464,8 @@ WaveTrackView::DoDetailedHitTest
 
 auto WaveTrackView::GetDisplays() const -> std::vector<WaveTrackDisplay>
 {
+   BuildSubViews();
+
    // Collect the display types of visible views and sort them by position
    using Pair = std::pair< int, WaveTrackDisplay >;
    std::vector< Pair > pairs;
@@ -146,6 +484,12 @@ auto WaveTrackView::GetDisplays() const -> std::vector<WaveTrackDisplay>
 }
 
 void WaveTrackView::SetDisplay(WaveTrackDisplay display)
+{
+   BuildSubViews();
+   DoSetDisplay( display );
+}
+
+void WaveTrackView::DoSetDisplay(WaveTrackDisplay display)
 {
    size_t ii = 0;
    WaveTrackSubViews::ForEach( [&,display]( WaveTrackSubView &subView ){
@@ -159,18 +503,22 @@ void WaveTrackView::SetDisplay(WaveTrackDisplay display)
 
 auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement
 {
-   // Collect the visible views
-   using Pair = std::pair< float, std::shared_ptr< TrackView > >;
+   BuildSubViews();
+
+   Refinement results;
+
+   // Collect the visible views in the right sequence
+   using Pair = std::pair< float*, std::shared_ptr< TrackView > >;
    std::vector< Pair > pairs( mPlacements.size() );
    size_t ii = 0;
    float total = 0;
    WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){
-      const auto &placement = mPlacements[ii];
+      auto &placement = mPlacements[ii];
       auto index = placement.index;
-      auto fraction = placement.fraction;
+      auto &fraction = placement.fraction;
       if ( index >= 0 && fraction > 0.0 )
          total += fraction,
-         pairs[ index ] = { fraction, subView.shared_from_this() };
+         pairs[ index ] = { &fraction, subView.shared_from_this() };
       ++ii;
    } );
 
@@ -179,17 +527,27 @@ auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement
      newEnd = std::remove_if( begin, end,
         []( const Pair &item ){ return !item.second; } );
    pairs.erase( newEnd, end );
+   results.reserve( pairs.size() );
 
    // Assign coordinates
-   Refinement results;
-   results.reserve( pairs.size() );
-   float partial = 0;
+   // Also update the stored placements, redenominating to the total height,
+   // storing integer values
    const auto top = rect.GetTop();
    const auto height = rect.GetHeight();
+   float partial = 0;
+   wxCoord lastCoord = 0;
+   float *lastFraction = nullptr;
    for ( const auto &pair : pairs ) {
-      results.emplace_back( top + (partial / total) * height, pair.second );
-      partial += pair.first;
+      wxCoord newCoord = top + (partial / total) * height;
+      results.emplace_back( newCoord, pair.second );
+      partial += *pair.first;
+      if (lastFraction)
+        *lastFraction = newCoord - lastCoord;
+      lastFraction = pair.first;
+      lastCoord = newCoord;
    }
+   if ( lastFraction )
+      *lastFraction = top + height - lastCoord;
 
    return results;
 }
@@ -197,6 +555,8 @@ auto WaveTrackView::GetSubViews( const wxRect &rect ) -> Refinement
 std::vector< std::shared_ptr< WaveTrackSubView > >
 WaveTrackView::GetAllSubViews()
 {
+   BuildSubViews();
+
    std::vector< std::shared_ptr< WaveTrackSubView > > results;
    WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){
       results.push_back( std::static_pointer_cast<WaveTrackSubView>(
@@ -207,6 +567,8 @@ WaveTrackView::GetAllSubViews()
 
 void WaveTrackView::DoSetMinimized( bool minimized )
 {
+   BuildSubViews();
+
    // May come here.  Invoke also on sub-views.
    TrackView::DoSetMinimized( minimized );
    WaveTrackSubViews::ForEach( [minimized](WaveTrackSubView &subView){
@@ -416,12 +778,41 @@ ClipParameters::ClipParameters
 
 void WaveTrackView::Reparent( const std::shared_ptr<Track> &parent )
 {
+   // BuildSubViews(); // not really needed
    CommonTrackView::Reparent( parent );
    WaveTrackSubViews::ForEach( [&parent](WaveTrackSubView &subView){
       subView.Reparent( parent );
    } );
 }
 
+void WaveTrackView::BuildSubViews() const
+{
+   if ( WaveTrackSubViews::size() == 0) {
+      // On-demand steps that can't happen in the constructor
+      auto pThis = const_cast<WaveTrackView*>( this );
+      pThis->BuildAll();
+      pThis->mPlacements.resize( WaveTrackSubViews::size() );
+      bool minimized = GetMinimized();
+      pThis->WaveTrackSubViews::ForEach( [&]( WaveTrackSubView &subView ){
+         subView.DoSetMinimized( minimized );
+      } );
+
+      auto pTrack = pThis->FindTrack();
+      auto display = TracksPrefs::ViewModeChoice();
+
+      // Force creation always:
+      WaveformSettings &settings = static_cast< WaveTrack* >( pTrack.get() )
+         ->GetIndependentWaveformSettings();
+
+      if (display == WaveTrackViewConstants::obsoleteWaveformDBDisplay) {
+         display = WaveTrackViewConstants::Waveform;
+         settings.scaleType = WaveformSettings::stLogarithmic;
+      }
+
+      pThis->DoSetDisplay( display );
+   }
+}
+
 void WaveTrackView::Draw(
    TrackPanelDrawingContext &context,
    const wxRect &rect, unsigned iPass )
diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h
index 00dd86c45..65b0bbf42 100644
--- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h
+++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackView.h
@@ -16,10 +16,13 @@ Paul Licameli split from class WaveTrack
 namespace WaveTrackViewConstants{ enum Display : int; }
 
 class WaveTrack;
+class WaveTrackView;
+
 class WaveTrackSubView : public CommonTrackView
 {
 public:
-   using CommonTrackView::CommonTrackView;
+   explicit
+   WaveTrackSubView( WaveTrackView &waveTrackView );
    
    virtual WaveTrackViewConstants::Display SubViewType() const = 0;
 
@@ -31,7 +34,9 @@ public:
       const AudacityProject *pProject, int currentTool, bool bMultiTool,
       const std::shared_ptr<WaveTrack> &wt,
       CommonTrackView &view);
-};
+private:
+   std::weak_ptr<WaveTrackView> mwWaveTrackView;
+   };
 
 struct WaveTrackSubViewPlacement {
    int index;
@@ -91,6 +96,9 @@ public:
    std::vector< std::shared_ptr< WaveTrackSubView > > GetAllSubViews();
 
 private:
+   void BuildSubViews() const;
+   void DoSetDisplay(WaveTrackDisplay display);
+
    // TrackPanelDrawable implementation
    void Draw(
       TrackPanelDrawingContext &context,
diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp
index 0d02853ce..45eb16792 100644
--- a/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp
+++ b/src/tracks/playabletrack/wavetrack/ui/WaveformView.cpp
@@ -1084,6 +1084,6 @@ void WaveformView::Draw(
 
 static const WaveTrackSubViews::RegisteredFactory key{
    []( WaveTrackView &view ){
-      return std::make_shared< WaveformView >( view.FindTrack() );
+      return std::make_shared< WaveformView >( view );
    }
 };