Use computed registry items for conditionally shown popup menu items
This commit is contained in:
parent
e07b5df6f3
commit
46f958f376
|
@ -550,13 +550,12 @@ void RateMenuTable::OnRateOther(wxCommandEvent &)
|
|||
|
||||
//=============================================================================
|
||||
// Class defining common command handlers for mono and stereo tracks
|
||||
struct WaveTrackMenuTable : PopupMenuTable
|
||||
struct WaveTrackMenuTable : ComputedPopupMenuTable< WaveTrackMenuTable >
|
||||
{
|
||||
static WaveTrackMenuTable &Instance( Track * pTrack);
|
||||
Track * mpTrack{};
|
||||
static WaveTrackMenuTable &Instance();
|
||||
|
||||
WaveTrackMenuTable()
|
||||
: PopupMenuTable{ "WaveTrack" }
|
||||
: ComputedPopupMenuTable< WaveTrackMenuTable >{ "WaveTrack" }
|
||||
{}
|
||||
|
||||
void InitUserData(void *pUserData) override;
|
||||
|
@ -586,15 +585,9 @@ struct WaveTrackMenuTable : PopupMenuTable
|
|||
void OnSplitStereoMono(wxCommandEvent & event);
|
||||
};
|
||||
|
||||
WaveTrackMenuTable &WaveTrackMenuTable::Instance( Track * pTrack )
|
||||
WaveTrackMenuTable &WaveTrackMenuTable::Instance()
|
||||
{
|
||||
static WaveTrackMenuTable instance;
|
||||
// Clear it out so we force a repopulate
|
||||
instance.Clear();
|
||||
// Ensure we know how to populate.
|
||||
// Messy, but the design does not seem to offer an alternative.
|
||||
// We won't use pTrack after populate.
|
||||
instance.mpTrack = pTrack;
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
@ -614,8 +607,7 @@ static std::vector<WaveTrackSubViewType> AllTypes()
|
|||
}
|
||||
|
||||
BEGIN_POPUP_MENU(WaveTrackMenuTable)
|
||||
// Functions usable "now" (list population time) and also "later"
|
||||
// (in callbacks to check and disable items)
|
||||
// Functions usable in callbacks to check and disable items
|
||||
static const auto findTrack =
|
||||
[]( PopupMenuHandler &handler ) -> WaveTrack & {
|
||||
return *static_cast< WaveTrack* >(
|
||||
|
@ -635,20 +627,26 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
|
|||
ProjectAudioIO::Get( project ).IsAudioActive();
|
||||
};
|
||||
|
||||
const auto pTrack = &findTrack( *this );
|
||||
const auto &view = WaveTrackView::Get( *pTrack );
|
||||
|
||||
BeginSection( "SubViews" );
|
||||
if ( WaveTrackSubViews::slots() > 1 )
|
||||
AppendCheckItem( "MultiView", OnMultiViewID, XO("&Multi-view"),
|
||||
POPUP_MENU_FN( OnMultiView ),
|
||||
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
|
||||
auto &track = findTrack( handler );
|
||||
const auto &view = WaveTrackView::Get( track );
|
||||
menu.Check( id, view.GetMultiView() );
|
||||
}
|
||||
);
|
||||
// Multi-view check mark item, if more than one track sub-view type is
|
||||
// known
|
||||
Append( []( My &table ) -> Registry::BaseItemPtr {
|
||||
if ( WaveTrackSubViews::slots() > 1 )
|
||||
return std::make_unique<Entry>(
|
||||
"MultiView", Entry::CheckItem, OnMultiViewID, XO("&Multi-view"),
|
||||
POPUP_MENU_FN( OnMultiView ),
|
||||
table,
|
||||
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
|
||||
auto &track = findTrack( handler );
|
||||
const auto &view = WaveTrackView::Get( track );
|
||||
menu.Check( id, view.GetMultiView() );
|
||||
} );
|
||||
else
|
||||
return nullptr;
|
||||
} );
|
||||
|
||||
// Append either a checkbox or radio item for each sub-view.
|
||||
// Radio buttons if in single-view mode, else checkboxes
|
||||
int id = OnSetDisplayId;
|
||||
for ( const auto &type : AllTypes() ) {
|
||||
static const auto initFn = []( bool radio ){ return
|
||||
|
@ -681,54 +679,66 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
|
|||
menu.Enable( id, false );
|
||||
};
|
||||
};
|
||||
if ( view.GetMultiView() ) {
|
||||
AppendCheckItem( type.name.Internal(), id++, type.name.Msgid(),
|
||||
POPUP_MENU_FN( OnSetDisplay ), initFn( false ) );
|
||||
}
|
||||
else {
|
||||
AppendRadioItem( type.name.Internal(), id++, type.name.Msgid(),
|
||||
POPUP_MENU_FN( OnSetDisplay ), initFn( true ) );
|
||||
}
|
||||
Append( [type, id]( My &table ) -> Registry::BaseItemPtr {
|
||||
const auto pTrack = &findTrack( table );
|
||||
const auto &view = WaveTrackView::Get( *pTrack );
|
||||
const auto itemType =
|
||||
view.GetMultiView() ? Entry::CheckItem : Entry::RadioItem;
|
||||
return std::make_unique<Entry>( type.name.Internal(), itemType,
|
||||
id, type.name.Msgid(),
|
||||
POPUP_MENU_FN( OnSetDisplay ), table,
|
||||
initFn( !view.GetMultiView() ) );
|
||||
} );
|
||||
++id;
|
||||
}
|
||||
EndSection();
|
||||
|
||||
if ( pTrack ) {
|
||||
|
||||
// Conditionally add sub-menu for wave color, if showing waveform
|
||||
Append( []( My &table ) -> Registry::BaseItemPtr {
|
||||
const auto pTrack = &findTrack( table );
|
||||
const auto &view = WaveTrackView::Get( *pTrack );
|
||||
const auto displays = view.GetDisplays();
|
||||
bool hasWaveform = (displays.end() != std::find(
|
||||
displays.begin(), displays.end(),
|
||||
WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} }
|
||||
) );
|
||||
if( hasWaveform ){
|
||||
BeginSection( "WaveColor" );
|
||||
POPUP_MENU_SUB_MENU( "WaveColor", WaveColorMenuTable, mpData )
|
||||
EndSection();
|
||||
}
|
||||
if( hasWaveform )
|
||||
return std::make_unique<PopupMenuSection>( "WaveColor",
|
||||
Registry::Shared( WaveColorMenuTable::Instance()
|
||||
.Get( table.mpData ) ) );
|
||||
else
|
||||
return nullptr;
|
||||
} );
|
||||
|
||||
// Conditionally add sub-menu for spectrogram settings, if showing spectrum
|
||||
Append( []( My &table ) -> Registry::BaseItemPtr {
|
||||
const auto pTrack = &findTrack( table );
|
||||
const auto &view = WaveTrackView::Get( *pTrack );
|
||||
const auto displays = view.GetDisplays();
|
||||
bool hasSpectrum = (displays.end() != std::find(
|
||||
displays.begin(), displays.end(),
|
||||
WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
|
||||
) );
|
||||
if( hasSpectrum ){
|
||||
if( hasSpectrum )
|
||||
// In future, we might move this to the context menu of the
|
||||
// Spectrum vertical ruler.
|
||||
// (But the latter won't be satisfactory without a means to
|
||||
// open that other context menu with keystrokes only, and that
|
||||
// would require some notion of a focused sub-view.)
|
||||
|
||||
BeginSection( "SpectrogramSettings" );
|
||||
AppendItem( "SpectrogramSettings", OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), POPUP_MENU_FN( OnSpectrogramSettings ),
|
||||
return std::make_unique<PopupMenuSection>( "SpectrogramSettings",
|
||||
std::make_unique<Entry>( "SpectrogramSettings", Entry::Item,
|
||||
OnSpectrogramSettingsID,
|
||||
XO("S&pectrogram Settings..."),
|
||||
POPUP_MENU_FN( OnSpectrogramSettings ), table,
|
||||
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
|
||||
// Bug 1253. Shouldn't open preferences if audio is busy.
|
||||
// We can't change them on the fly yet anyway.
|
||||
auto gAudioIO = AudioIOBase::Get();
|
||||
menu.Enable(id, !gAudioIO->IsBusy());
|
||||
}
|
||||
);
|
||||
EndSection();
|
||||
}
|
||||
|
||||
}
|
||||
} ) );
|
||||
else
|
||||
return nullptr;
|
||||
} );
|
||||
|
||||
BeginSection( "Channels" );
|
||||
// If these are enabled again, choose a hot key for Mono that does not conflict
|
||||
|
@ -791,7 +801,9 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
|
|||
POPUP_MENU_FN( OnSplitStereo ), enableSplitStereo );
|
||||
// DA: Uses split stereo track and then drag pan sliders for split-stereo-to-mono
|
||||
#ifndef EXPERIMENTAL_DA
|
||||
AppendItem( "SplitToMono", OnSplitStereoMonoID, XO("Split Stereo to Mo&no"), POPUP_MENU_FN( OnSplitStereoMono ), enableSplitStereo );
|
||||
AppendItem( "SplitToMono", OnSplitStereoMonoID,
|
||||
XO("Split Stereo to Mo&no"), POPUP_MENU_FN( OnSplitStereoMono ),
|
||||
enableSplitStereo );
|
||||
#endif
|
||||
EndSection();
|
||||
|
||||
|
@ -1126,7 +1138,7 @@ void WaveTrackMenuTable::OnSplitStereoMono(wxCommandEvent &)
|
|||
PopupMenuTable *WaveTrackControls::GetMenuExtension(Track * pTrack)
|
||||
{
|
||||
|
||||
WaveTrackMenuTable & result = WaveTrackMenuTable::Instance( pTrack );
|
||||
WaveTrackMenuTable & result = WaveTrackMenuTable::Instance();
|
||||
return &result;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,11 +43,11 @@ struct PopupMenu : public wxMenu
|
|||
void *pUserData;
|
||||
};
|
||||
|
||||
class PopupMenuBuilder : public MenuVisitor {
|
||||
class PopupMenuBuilder : public PopupMenuVisitor {
|
||||
public:
|
||||
explicit
|
||||
PopupMenuBuilder( PopupMenuTable &table, PopupMenu &menu, void *pUserData )
|
||||
: mTable{ table }
|
||||
: PopupMenuVisitor{ table }
|
||||
, mMenu{ &menu }
|
||||
, mRoot{ mMenu }
|
||||
, mpUserData{ pUserData }
|
||||
|
@ -58,7 +58,6 @@ public:
|
|||
void DoVisit( Registry::SingleItem &item, const Path &path ) override;
|
||||
void DoSeparator() override;
|
||||
|
||||
PopupMenuTable &mTable;
|
||||
std::vector< std::unique_ptr<PopupMenu> > mMenus;
|
||||
PopupMenu *mMenu, *mRoot;
|
||||
void *const mpUserData;
|
||||
|
@ -146,14 +145,18 @@ void PopupMenuTable::ExtendMenu( wxMenu &menu, PopupMenuTable &table )
|
|||
Registry::Visit( visitor, table.Get( theMenu.pUserData ).get() );
|
||||
}
|
||||
|
||||
void PopupMenuTable::Append( Registry::BaseItemPtr pItem )
|
||||
{
|
||||
mStack.back()->items.push_back( std::move( pItem ) );
|
||||
}
|
||||
|
||||
void PopupMenuTable::Append(
|
||||
const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
|
||||
const TranslatableString &string, wxCommandEventFunction memFn,
|
||||
const PopupMenuTableEntry::InitFunction &init )
|
||||
{
|
||||
mStack.back()->items.push_back( std::make_unique<Entry>(
|
||||
stringId, type, id, string, memFn, *this, init
|
||||
) );
|
||||
Append( std::make_unique<Entry>(
|
||||
stringId, type, id, string, memFn, *this, init ) );
|
||||
}
|
||||
|
||||
void PopupMenuTable::BeginSection( const Identifier &name )
|
||||
|
@ -172,11 +175,11 @@ void PopupMenuTable::EndSection()
|
|||
namespace{
|
||||
void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
|
||||
{
|
||||
class PopupMenuDestroyer : public MenuVisitor {
|
||||
class PopupMenuDestroyer : public PopupMenuVisitor {
|
||||
public:
|
||||
explicit
|
||||
PopupMenuDestroyer( PopupMenuTable &table, PopupMenu &menu )
|
||||
: mTable{ table }
|
||||
: PopupMenuVisitor{ table }
|
||||
, mMenu{ menu }
|
||||
{}
|
||||
|
||||
|
@ -188,7 +191,6 @@ void PopupMenu::DisconnectTable(PopupMenuTable *pTable)
|
|||
pEntry->handler.DestroyMenu();
|
||||
}
|
||||
|
||||
PopupMenuTable &mTable;
|
||||
PopupMenu &mMenu;
|
||||
};
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ struct PopupMenuTableEntry : Registry::SingleItem
|
|||
PopupMenuTableEntry( const Identifier &stringId,
|
||||
Type type_, int id_, const TranslatableString &caption_,
|
||||
wxCommandEventFunction func_, PopupMenuHandler &handler_,
|
||||
InitFunction init_ )
|
||||
InitFunction init_ = {} )
|
||||
: SingleItem{ stringId }
|
||||
, type(type_)
|
||||
, id(id_)
|
||||
|
@ -95,6 +95,11 @@ public:
|
|||
virtual void DestroyMenu() = 0;
|
||||
};
|
||||
|
||||
struct PopupMenuVisitor : public MenuVisitor {
|
||||
explicit PopupMenuVisitor( PopupMenuTable &table ) : mTable{ table } {}
|
||||
PopupMenuTable &mTable;
|
||||
};
|
||||
|
||||
class PopupMenuTable : public PopupMenuHandler
|
||||
{
|
||||
public:
|
||||
|
@ -120,7 +125,8 @@ public:
|
|||
|
||||
const std::shared_ptr< Registry::GroupItem > &Get( void *pUserData )
|
||||
{
|
||||
this->InitUserData( pUserData );
|
||||
if ( pUserData )
|
||||
this->InitUserData( pUserData );
|
||||
if (!mTop)
|
||||
Populate();
|
||||
return mTop;
|
||||
|
@ -130,6 +136,8 @@ protected:
|
|||
virtual void Populate() = 0;
|
||||
|
||||
// To be used in implementations of Populate():
|
||||
void Append( Registry::BaseItemPtr pItem );
|
||||
|
||||
void Append(
|
||||
const Identifier &stringId, PopupMenuTableEntry::Type type, int id,
|
||||
const TranslatableString &string, wxCommandEventFunction memFn,
|
||||
|
@ -156,14 +164,38 @@ protected:
|
|||
void BeginSection( const Identifier &name );
|
||||
void EndSection();
|
||||
|
||||
void Clear() { mTop.reset(); }
|
||||
|
||||
std::shared_ptr< Registry::GroupItem > mTop;
|
||||
std::vector< Registry::GroupItem* > mStack;
|
||||
Identifier mId;
|
||||
TranslatableString mCaption;
|
||||
};
|
||||
|
||||
// A "CRTP" class that injects a convenience function, which appends a menu item
|
||||
// computed lazily by a function that is passed the table (after it has stored
|
||||
// its user data)
|
||||
template< typename Derived >
|
||||
class ComputedPopupMenuTable : public PopupMenuTable
|
||||
{
|
||||
public:
|
||||
using PopupMenuTable::PopupMenuTable;
|
||||
using PopupMenuTable::Append;
|
||||
|
||||
// Appends a computed item, which may be omitted when function returns null
|
||||
// and thus can be a conditional item
|
||||
using Factory = std::function< Registry::BaseItemPtr( Derived& ) >;
|
||||
void Append( const Factory &factory )
|
||||
{
|
||||
using namespace Registry;
|
||||
Append( std::make_unique< ComputedItem >(
|
||||
[factory]( Visitor &baseVisitor ){
|
||||
auto &visitor = static_cast< PopupMenuVisitor& >( baseVisitor );
|
||||
auto &table = static_cast< Derived& >( visitor.mTable );
|
||||
return factory( table );
|
||||
}
|
||||
) );
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
The following macros make it easy to attach a popup menu to a window.
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user