mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-04 17:49:45 +02:00
Break MenuCreator::CreateMenusAndCommands() into many little pieces...
... These pieces are mostly declarative, not procedural, and CreateMenusAndCommands shrinks to a table-driven interpretation loop and little else. The pieces still all live in the still huge Menus.cpp, but are fit to be cut and pasted into many smaller files, taking the handler functions with them. As that is done, it would also be good to break MenuCommandHandler into many smaller classes. Some of these handlers might be stateless and so would require only a singleton object, not a per-project object. But others would require new fields in AudacityProject analogous to mMenuCommandHandler. Still to do is to devise a system of dynamic registration building up the tables so that Menus.cpp would not have compilation and link dependencies on each of the pieces, and similarly a registration of factories for the extra packages of state in the handler objects attached to each AudacityProject.
This commit is contained in:
commit
f70c76de83
2814
src/Menus.cpp
2814
src/Menus.cpp
File diff suppressed because it is too large
Load Diff
@ -362,7 +362,9 @@ private:
|
||||
wxMenuBar * CurrentMenuBar() const;
|
||||
wxMenuBar * GetMenuBar(const wxString & sMenu) const;
|
||||
wxMenu * CurrentSubMenu() const;
|
||||
public:
|
||||
wxMenu * CurrentMenu() const;
|
||||
private:
|
||||
wxString GetLabel(const CommandListEntry *entry) const;
|
||||
wxString GetLabelWithDisabledAccel(const CommandListEntry *entry) const;
|
||||
|
||||
@ -398,4 +400,236 @@ private:
|
||||
std::unique_ptr< wxMenuBar > mTempMenuBar;
|
||||
};
|
||||
|
||||
// Define items that populate tables that describe menu trees
|
||||
namespace MenuTable {
|
||||
// TODO C++17: maybe use std::variant (discriminated unions) to achieve
|
||||
// polymorphism by other means, not needing unique_ptr and dynamic_cast
|
||||
// and using less heap.
|
||||
// Most items in the table will be the large ones describing commands, so the
|
||||
// waste of space in unions for separators and sub-menus should not be
|
||||
// large.
|
||||
struct BaseItem {
|
||||
// declare at least one virtual function so dynamic_cast will work
|
||||
virtual ~BaseItem();
|
||||
};
|
||||
using BaseItemPtr = std::unique_ptr<BaseItem>;
|
||||
using BaseItemPtrs = std::vector<BaseItemPtr>;
|
||||
|
||||
|
||||
// The type of functions that generate menu table descriptions.
|
||||
// Return type is a shared_ptr to let the function decide whether to recycle
|
||||
// the object or rebuild it on demand each time.
|
||||
// Return value from the factory may be null.
|
||||
using Factory = std::function<
|
||||
std::shared_ptr< MenuTable::BaseItem >( AudacityProject & )
|
||||
>;
|
||||
|
||||
struct ComputedItem : BaseItem {
|
||||
explicit ComputedItem( const Factory &factory_ )
|
||||
: factory{ factory_ }
|
||||
{}
|
||||
~ComputedItem() override;
|
||||
|
||||
Factory factory;
|
||||
};
|
||||
|
||||
struct GroupItem : BaseItem {
|
||||
// Construction from a previously built-up vector of pointers
|
||||
GroupItem( BaseItemPtrs &&items_ );
|
||||
// In-line, variadic constructor that doesn't require building a vector
|
||||
template< typename... Args >
|
||||
GroupItem( Args&&... args )
|
||||
{ Append( std::forward< Args >( args )... ); }
|
||||
~GroupItem() override;
|
||||
|
||||
BaseItemPtrs items;
|
||||
|
||||
private:
|
||||
// nullary overload grounds the recursion
|
||||
void Append() {}
|
||||
// recursive overload
|
||||
template< typename Arg, typename... Args >
|
||||
void Append( Arg &&arg, Args&&... moreArgs )
|
||||
{
|
||||
// Dispatch one argument to the proper overload of AppendOne.
|
||||
// std::forward preserves rvalue/lvalue distinction of the actual
|
||||
// argument of the constructor call; that is, it inserts a
|
||||
// std::move() if and only if the original argument is rvalue
|
||||
AppendOne( std::forward<Arg>( arg ) );
|
||||
// recur with the rest of the arguments
|
||||
Append( std::forward<Args>(moreArgs)... );
|
||||
};
|
||||
|
||||
void AppendOne( BaseItemPtr&& ptr );
|
||||
// This override allows a lambda or function pointer in the variadic
|
||||
// argument lists without any other syntactic wrapping:
|
||||
void AppendOne( const Factory &factory )
|
||||
{ AppendOne( std::make_unique<ComputedItem>( factory ) ); }
|
||||
};
|
||||
|
||||
struct MenuItem final : GroupItem {
|
||||
// Construction from a previously built-up vector of pointers
|
||||
MenuItem( const wxString &title_, BaseItemPtrs &&items_ );
|
||||
// In-line, variadic constructor that doesn't require building a vector
|
||||
template< typename... Args >
|
||||
MenuItem( const wxString &title_, Args&&... args )
|
||||
: GroupItem{ std::forward<Args>(args)... }
|
||||
, title{ title_ }
|
||||
{}
|
||||
~MenuItem() override;
|
||||
|
||||
wxString title; // translated
|
||||
};
|
||||
|
||||
struct ConditionalGroupItem final : GroupItem {
|
||||
using Condition = std::function< bool() >;
|
||||
|
||||
// Construction from a previously built-up vector of pointers
|
||||
ConditionalGroupItem( Condition condition_, BaseItemPtrs &&items_ );
|
||||
// In-line, variadic constructor that doesn't require building a vector
|
||||
template< typename... Args >
|
||||
ConditionalGroupItem( Condition condition_, Args&&... args )
|
||||
: GroupItem{ std::forward<Args>(args)... }
|
||||
, condition{ condition_ }
|
||||
{}
|
||||
~ConditionalGroupItem() override;
|
||||
|
||||
Condition condition;
|
||||
};
|
||||
|
||||
struct SeparatorItem final : BaseItem
|
||||
{
|
||||
~SeparatorItem() override;
|
||||
};
|
||||
|
||||
struct CommandItem final : BaseItem {
|
||||
CommandItem(const wxString &name_,
|
||||
const wxString &label_in_,
|
||||
bool hasDialog_,
|
||||
CommandHandlerFinder finder_,
|
||||
CommandFunctorPointer callback_,
|
||||
CommandFlag flags_,
|
||||
const CommandManager::Options &options_);
|
||||
~CommandItem() override;
|
||||
|
||||
const wxString name;
|
||||
const wxString label_in;
|
||||
bool hasDialog;
|
||||
CommandHandlerFinder finder;
|
||||
CommandFunctorPointer callback;
|
||||
CommandFlag flags;
|
||||
CommandManager::Options options;
|
||||
};
|
||||
|
||||
struct CommandGroupItem final : BaseItem {
|
||||
CommandGroupItem(const wxString &name_,
|
||||
const IdentInterfaceSymbol items_[],
|
||||
size_t nItems_,
|
||||
CommandHandlerFinder finder_,
|
||||
CommandFunctorPointer callback_,
|
||||
CommandFlag flags_,
|
||||
bool isEffect_);
|
||||
~CommandGroupItem() override;
|
||||
|
||||
const wxString name;
|
||||
const std::vector<IdentInterfaceSymbol> items;
|
||||
CommandHandlerFinder finder;
|
||||
CommandFunctorPointer callback;
|
||||
CommandFlag flags;
|
||||
bool isEffect;
|
||||
};
|
||||
|
||||
// For manipulating the enclosing menu or sub-menu directly,
|
||||
// adding any number of items, not using the CommandManager
|
||||
struct SpecialItem final : BaseItem
|
||||
{
|
||||
using Appender = std::function< void( AudacityProject&, wxMenu& ) >;
|
||||
|
||||
explicit SpecialItem( const Appender &fn_ )
|
||||
: fn{ fn_ }
|
||||
{}
|
||||
~SpecialItem() override;
|
||||
|
||||
Appender fn;
|
||||
};
|
||||
|
||||
// Following are the functions to use directly in writing table definitions.
|
||||
|
||||
// Group items can be constructed two ways.
|
||||
// Pointers to subordinate items are moved into the result.
|
||||
// Null pointers are permitted, and ignored when building the menu.
|
||||
// Items are spliced into the enclosing menu
|
||||
template< typename... Args >
|
||||
inline BaseItemPtr Items( Args&&... args )
|
||||
{ return std::make_unique<GroupItem>(
|
||||
std::forward<Args>(args)... ); }
|
||||
inline BaseItemPtr Items(
|
||||
const wxString &title, BaseItemPtrs &&items )
|
||||
{ return std::make_unique<GroupItem>( std::move( items ) ); }
|
||||
|
||||
// Menu items can be constructed two ways, as for group items
|
||||
// Items will appear in a main toolbar menu or in a sub-menu
|
||||
template< typename... Args >
|
||||
inline BaseItemPtr Menu(
|
||||
const wxString &title, Args&&... args )
|
||||
{ return std::make_unique<MenuItem>(
|
||||
title, std::forward<Args>(args)... ); }
|
||||
inline BaseItemPtr Menu(
|
||||
const wxString &title, BaseItemPtrs &&items )
|
||||
{ return std::make_unique<MenuItem>( title, std::move( items ) ); }
|
||||
|
||||
// Conditional group items can be constructed two ways, as for group items
|
||||
// These items register in the CommandManager but are not shown in menus
|
||||
template< typename... Args >
|
||||
inline BaseItemPtr ConditionalItems(
|
||||
ConditionalGroupItem::Condition condition, Args&&... args )
|
||||
{ return std::make_unique<ConditionalGroupItem>(
|
||||
condition, std::forward<Args>(args)... ); }
|
||||
inline BaseItemPtr ConditionalItems(
|
||||
ConditionalGroupItem::Condition condition, BaseItemPtrs &&items )
|
||||
{ return std::make_unique<ConditionalGroupItem>(
|
||||
condition, std::move( items ) ); }
|
||||
|
||||
// Make either a menu item or just a group, depending on the nonemptiness
|
||||
// of the title
|
||||
template< typename... Args >
|
||||
inline BaseItemPtr MenuOrItems(
|
||||
const wxString &title, Args&&... args )
|
||||
{ if ( title.empty() ) return Items( std::forward<Args>(args)... );
|
||||
else return std::make_unique<MenuItem>(
|
||||
title, std::forward<Args>(args)... ); }
|
||||
inline BaseItemPtr MenuOrItems(
|
||||
const wxString &title, BaseItemPtrs &&items )
|
||||
{ if ( title.empty() ) return Items( std::move( items ) );
|
||||
else return std::make_unique<MenuItem>( title, std::move( items ) ); }
|
||||
|
||||
inline std::unique_ptr<SeparatorItem> Separator()
|
||||
{ return std::make_unique<SeparatorItem>(); }
|
||||
|
||||
inline std::unique_ptr<CommandItem> Command(
|
||||
const wxString &name, const wxString &label_in, bool hasDialog,
|
||||
CommandHandlerFinder finder, CommandFunctorPointer callback,
|
||||
CommandFlag flags, const CommandManager::Options &options = {})
|
||||
{
|
||||
return std::make_unique<CommandItem>(
|
||||
name, label_in, hasDialog, finder, callback, flags, options
|
||||
);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<CommandGroupItem> CommandGroup(
|
||||
const wxString &name,
|
||||
const IdentInterfaceSymbol items[], size_t nItems,
|
||||
CommandHandlerFinder finder, CommandFunctorPointer callback,
|
||||
CommandFlag flags, bool isEffect = false)
|
||||
{
|
||||
return std::make_unique<CommandGroupItem>(
|
||||
name, items, nItems, finder, callback, flags, isEffect
|
||||
);
|
||||
}
|
||||
|
||||
inline std::unique_ptr<SpecialItem> Special(
|
||||
const SpecialItem::Appender &fn )
|
||||
{ return std::make_unique<SpecialItem>( fn ); }
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1156,14 +1156,13 @@ bool Scrubber::CanScrub() const
|
||||
static CommandHandlerObject &findme(AudacityProject &project)
|
||||
{ return project.GetScrubber(); }
|
||||
|
||||
void Scrubber::AddMenuItems()
|
||||
MenuTable::BaseItemPtr Scrubber::Menu()
|
||||
{
|
||||
auto cm = mProject->GetCommandManager();
|
||||
using Options = CommandManager::Options;
|
||||
|
||||
cm->BeginMenu( _("Scru&bbing") );
|
||||
MenuTable::BaseItemPtrs ptrs;
|
||||
for (const auto &item : menuItems) {
|
||||
cm->AddItem( item.name, wxGetTranslation(item.label),
|
||||
ptrs.push_back( MenuTable::Command( item.name, wxGetTranslation(item.label),
|
||||
// No menu items yet have dialogs
|
||||
false,
|
||||
findme, static_cast<CommandFunctorPointer>(item.memFn),
|
||||
@ -1172,9 +1171,11 @@ void Scrubber::AddMenuItems()
|
||||
? // a checkmark item
|
||||
Options{}.CheckState( (this->*item.StatusTest)() )
|
||||
: // not a checkmark item
|
||||
Options{} );
|
||||
Options{}
|
||||
) );
|
||||
}
|
||||
cm->EndMenu();
|
||||
|
||||
return MenuTable::Menu( _("Scru&bbing"), std::move( ptrs ) );
|
||||
}
|
||||
|
||||
void Scrubber::PopulatePopupMenu(wxMenu &menu)
|
||||
|
@ -20,6 +20,7 @@ Paul Licameli split from TrackPanel.cpp
|
||||
#include "../../widgets/Overlay.h"
|
||||
#include "../../commands/CommandFunctors.h"
|
||||
#include "../../commands/CommandContext.h"
|
||||
#include "../../commands/CommandManager.h" // for MenuTable
|
||||
#include "../../../include/audacity/Types.h"
|
||||
|
||||
class AudacityProject;
|
||||
@ -127,7 +128,7 @@ public:
|
||||
bool CanScrub() const;
|
||||
|
||||
// For the toolbar
|
||||
void AddMenuItems();
|
||||
MenuTable::BaseItemPtr Menu();
|
||||
// For popup
|
||||
void PopulatePopupMenu(wxMenu &menu);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user