No answer…
Maybe I asked too silly question and it made no sense.
So can I ask a different question?
I’ll try to make things more concretely.
Here’s my code that is simplified as possible as I can.
I mainly refer to Projucer(MyApplication
) and Tracktion Engine examples’ EditViewState(MyAppEngine
) class.
And I omit codes about command line mode to make it more easy to understand. Please forget about command line mode.
I hope that it shows my intention.
//================MyAppEngine.h================================
namespace IDs
{
#define DECLARE_ID(name) const juce::Identifier name (#name);
DECLARE_ID(MyAppParameters)
DECLARE_ID(MyToggleButtonState)
#undef DECLARE_ID
}
class MyAppEngine
{
public:
MyAppEngine()
{
MyToggleButtonState.referTo(params, IDs::MyToggleButtonState, &undo, false);
}
ValueTree params{ "MyAppParams" };
CachedValue<bool> MyToggleButtonState;
UndoManager undo;
}
//================MyApplication.h================================
class MyApplication : public JUCEApplication
{
public:
MyApplication() = default;
static MyApplication& getApp();
/*...inherited member functions from JUCEApplication...*/
enum MyCommandIDs
{
Open = 1,
Save,
Undo,
Redo,
ChangeMyToggleButtonState
};
std::unique_ptr<ApplicationCommandManager> commandManager;
std::unique_ptr<MyAppEngine> engine;
private:
std::unique_ptr<DocumentWindow> mainWindow;
};
//================Maincomponent.h================================
class Maincomponent : public juce::Component
, public ApplicationCommandTarget
, public ValueTree::Listener
{
Maincomponent(MyAppEngine& engine);
private:
MyAppEngine& engine;
ToggleButton MyToggleButton;
/*...inherited member functions...*/
}
//================Maincomponent.cpp================================
Maincomponent::Maincomponent()
:engine(*MyApplication::getApp().engine)
{
MyToggleButton.onClick = [this] { engine.MyToggleButtonState = !engine.MyToggleButtonState.get();;};
}
void MainComponent::valueTreePropertyChanged(ValueTree& , const Identifier& id)
{
if(id == IDs::MyToggleButtonState)
{
MyToggleButton.setToggleState(engine.MyToggleButtonState.get(), dontsendNotification);
}
}
ApplicationCommandTarget* MainComponent::getNextCommandTarget() { return nullptr; }
void MainComponent::getAllCommands(Array<CommandID>& commands)
{
Array<CommandID> ids
{
MyApplication::Open ,
MyApplication::Save ,
MyApplication::Undo ,
MyApplication::Redo ,
MyApplication::ChangeMyToggleButtonState ,
};
commands.addArray(ids);
}
void MainComponent::getCommandInfo(CommandID commandID, ApplicationCommandInfo& result)
{
switch (commandID)
{
case MyApplication::Open:
{
result.setInfo("Open", Open_Script, "File", 0);
auto k = KeyPress::createFromDescription("ctrl + o");
result.addDefaultKeypress(k.getKeyCode(), k.getModifiers());
break;
}
case MyApplication::Save:
{
result.setInfo("Save", Save_Script, "File", 0);
auto k = KeyPress::createFromDescription("ctrl + Shift + s");
result.addDefaultKeypress(k.getKeyCode(), k.getModifiers());
break;
}
case MyApplication::Undo:
{
result.setInfo("Undo", Undo_Script, "Edit", 0);
auto k = KeyPress::createFromDescription("ctrl + z");
result.addDefaultKeypress(k.getKeyCode(), k.getModifiers());
break;
}
case MyApplication::Redo:
{
result.setInfo("Redo", Redo_Script, "Edit", 0);
auto k = KeyPress::createFromDescription("ctrl + y");
result.addDefaultKeypress(k.getKeyCode(), k.getModifiers());
break;
}
case MyApplication::ChangeMyToggleButtonState:
{
result.setInfo("ChangeMyToggleButtonState", ChangeMyToggleButtonState_Script, "Edit", 0);
auto k = KeyPress::createFromDescription("ctrl + 0");
result.addDefaultKeypress(k.getKeyCode(), k.getModifiers());
break;
}
default:
break;
}
//I often use Utils namespace to static functions
bool MainComponent::perform(const InvocationInfo& info)
{
switch (info.commandID)
{
case MyApplication::Open:
Utils::OpenProject(engine);
break;
case MyApplication::SaveAsPreset:
Utils::save(engine);
break;
case MyApplication::Undo:
if(engine.undo.canUndo())
engine.undo.undo();
break;
case MyApplication::Redo:
if(engine.undo.canRedo())
engine.undo.redo();
break;
case MyApplication::ChangeMyToggleButtonState:
engine.MyToggleButtonState = engine.MyToggleButtonState.get();
break;
default:
return false;
}
return true;
}
//================MyApp.cpp================================
void MyApplication::initialise(const juce::String&)
{
engine = std::make_unique<MyAppEngine>();
commandManager = std::make_unique<ApplicationCommandManager>();
commandManager->registerAllCommandsForTarget(this);
{
//register first so that all widgets can receive command description.
MainComponent comp;
commandManager->registerAllCommandsForTarget(&comp);
//destruct temporary MainComponent
}
mainWindow = std::make_unique<MainWindow>();//mainWindow is content owning MainComponent at constructor.
mainWindow->grabKeyboardFocus();
mainWindow->addKeyListener(commandManager->getKeyMappings());
}
My problem is,
- I have to manage both enum(
MyApplication::ChangeMyToggleButtonState
) and Identifier( IDs::MyToggleButtonState
) at the same time.
- I have to write
engine.MyToggleButtonState = !engine.MyToggleButtonState.get();
twice, at MyToggleButton.onClick
and at bool MainComponent::perform(const InvocationInfo& info)
1 is about enum vs Identifier. Of course both are not the same. But my suggestion is, how about managing ApplicationCommandManager
with Identifier? I think it’s sensible question, but I’m simply curious if there is any problem to change enum to Identifier.
2 is about invoking command manager to run a command. I couldn’t find a way, something like commandManager.invoke(MyApplication::ChangeMyToggleButtonState)
then I can manage “ChangeMyToggleButtonState” command in one place.
Or, if there is a std::function in ApplicationCommandInfo
and InvocationInfo
it would be more neat. Is it a bad design?