ApplicationCommandManager to invoke a command, the command ends up in some
ApplicationCommandTarget, where it can be executed using the
perform(const InvocationInfo&) override method. But from what I can tell, there is no direct way of specifying arguments for the command you want to perform, which means you are limited to using
function(void) functions. I can understand why this is, but I am wondering if there is any way around it? In other words, what I want to do is this:
//Inside an ApplicationCommandTarget object:
bool perform (const InvocationInfo& info) override
case CommandIDs::resize :
resize(newSize); // <--- But where does the "newSize" argument come from?
I can think of a few hacks for this–for instance, somehow retrieving
InvocationInfo.originatingComponent–but it seems a bit dodgy. Has anyone ever used any other workarounds? Would I be abusing the
ApplicationCommandTarget if I tried?
Maybe one solution would be to store your commands in a map like :
And then look for and execute the commands with the help of that, instead of the switch case statement. Because the map stores std::functions, you can put in C++11 lambdas that can capture any state that the executable functions might need, even though the function signature itself is void(void).
As I understand it, the ApplicationCommandManager is designed for PopupMenus, Application menus and Keyboard Shortcuts. Neither of them offer the option to specify arguments as a user.
What is the use case, and what benefit would you expect using the ApplicationCommandManager over a regular function call with normal arguments?
I was thinking specifically about a commands to resize a grid (ie. add rows or columns to it), and to zoom in or out of a component. I was going to try to do this through right-click popup menus (and I guess I hadn’t gotten to the bit where I have to work out how to get values in through the menu system!)
What I was hoping to achieve was uniformity in the code, so that all of the commands are implemented in a uniform way, rather than being spread all over the place. But I can see that this also runs the risk of making things unnecessarily complicated.
I like @Xenakios’s idea of using a map, although std::function might be a bit heavyweight in this case. I can’t see myself going beyond passing in a single float as an argument (or maybe two for two-dimensional zoom).
I don’t think std::function is heavyweight… @Xenakios idea might be worth exploring.
Alternatively, I would add a command “increase size” which would multiply by 1.1 (adding 10%) and decrease the same way…
I think I hadn’t understood @Xenakios’s solution the first time around, and you’re right that it’s elegant. Come to think of it though, couldn’t I bypass the map and just put lambdas directly in the switch statement?
That wouldn’t necessarily be helpful in any way. While you could do it, it would likely effectively be the same as just writing the same code directly in the switch statement, just in a more complicated fashion. (There are some cases where a lambda might be useful, but I am not sure if it would be useful in your use case.)
My suggested std::map based solution assumed you are going to put the map as a member variable of some class and init the the functions in your constructor or similar suitable location in the code.
I don’t think it would necessarily be that bad to use the originatingComponent member of the InvocationInfo, either. Since it’s there, why not use it? You might not even need to do a dynamic cast on it to some particular subclass since Components have the dynamic properties, that can be got with getProperties().
Lol, it looks like I didn’t understand your idea twice! But in any case, I’m now realizing that there is another complication, in that the popup menu can’t be modal, so I’ll have to put it on a different thread. Is this likely to cause complications for either of the two methods we’ve been discussing? (I will of course be trying it out for myself, but this will be my first venture into threading, so I don’t really know what to expect).
Actually, perhaps I jumped the gun on the threading? The documentation for PopupMenu says that you can use ModalComponentManager::Callback to run the menu asynchronously. I was assuming that this involved threading, but I can’t see any mention of it in the documentation.
I’ve followed the advice on this post and so far everything seems to be running, though unfortunately I haven’t figured out a way to test it properly yet. My main aim here is to ensure that the menu doesn’t interfere with the running of the application in any way. Is an asynchronous menu using ModalComponentManager::Callback sufficient for this?
Absolutely. Actually I should have mentioned, modal is by no means a different thread. It means not calling the normal message queue, but staying in a local message loop. But it is still happening on the main GUI thread, since all Component methods like paint(), setBounds() etc. require a MessageManagerLock to avoid parallel access to the Component’s state, bounds and data.
I’ve got this working quite well, using the getProperties() method to set and get the arguments (this is a really nice feature of the Component class that I wouldn’t have spotted if @Xenakios hadn’t pointed it out).
I keep on running into a problem though that I’m unable to pinpoint. I am trying to query the current value of the variable as the menu is created, so that the menu can initialize correctly. This is what I am doing:
getFirstCommandTarget() to find the target that the PopupMenu will send commands to.
static_cast<MyClass*> (target) -> getMyValue() to read the needed value from the target class (which inherits from
#2 compiles correctly, and the cast doesn’t return null, but when it gets to
getMyValue() I get to runtime errors around a
referenceCountedObject. If the function definition for
return 6 then it works fine, but if it is
return myValue where
myValue is a member variable, it asserts. It’s as if the pointer leads to the class but not the object.
Can anyone think what might be going on? I know that my description is vague, but I’m unable to diagnose the problem any further at this point. Any sort of hint would be helpful.
There is no need to respond to my last message. I’m still not exactly sure what was going wrong, but I’ve found another way of solving the problem that bypasses the static_cast.