1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-24 08:10:05 +02:00

Split wave and spectrogram views...

To get a split view: move the mouse near the top or bottom of a wave track view
until the cursor changes to "north-south", then drag and release.

When a separation of views exists, the separation line can be dragged again.

You can drag until one of the sub-views shrinks to nothing.  There is a certain
snapping (now 5 pixels) to make this easier and prevent very small views shorter
than that.

Notes:

- ESC key before mouse release cancels the drag, as expected.
- Either wave or spectrum view can be on top, depending on how you drag.
- Please note, and test, that the vertical ruler also divides in two portions,
and that all mouse drag actions (including spectral selection) in the track
view or the ruler are available, and only those proper for the sub-view.
- If you have a stereo track, then the wave and spectrum views keep the same
height proportions in the two channels.
- If you want to drag the channel separator instead, unfortunately there is no
difference yet between the preview cursors.  North-south seems the most sensible
cursor for both of these drags, among pre-defined cursors.
- The drop-down menu of the track control panel may now have two checkmarks, for
spectrum and for either Waveform or Waveform dB.  Selecting any of those items
causes that view type to become the sole view.
- In my opinion, the behavior above is not satisfactory.  I think the more
consistent user interface would have only Waveform and Spectrum items as a
non-exclusive radio button choice in the menu, while the choice between linear
and dB waveform view should be part of the right-mouse popup menu in the
vertical ruler, analogous to the choice among scales for spectrum view.
- The 'VIEW property of *TRACK* in Nyquist is now a list rather than an atom
when there is a split view.  None of the existing .ny plug-ins that ship with
Audacity need to be changed, because they only ever test whether the property
is nil, indicating that an effect preview is being computed.
This commit is contained in:
Paul Licameli 2019-11-23 17:28:48 -05:00
commit 92bdec7a46
8 changed files with 492 additions and 48 deletions

View File

@ -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()

View File

@ -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 ) {

View File

@ -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 )
auto format = [&]( decltype(displays[0]) display ){
switch ( display )
{
case Waveform:
view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
break;
return (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
case Spectrum:
view = wxT("\"Spectrogram\"");
break;
default: view = wxT("NIL"); break;
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)

View File

@ -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 );
}
};

View File

@ -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()

View File

@ -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(
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 )

View File

@ -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,6 +34,8 @@ public:
const AudacityProject *pProject, int currentTool, bool bMultiTool,
const std::shared_ptr<WaveTrack> &wt,
CommonTrackView &view);
private:
std::weak_ptr<WaveTrackView> mwWaveTrackView;
};
struct WaveTrackSubViewPlacement {
@ -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,

View File

@ -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 );
}
};