Open Al12rs opened 4 years ago
Here's a link to the middle of a post from the last time this was discussed: https://discordapp.com/channels/265929299490635777/396267240267317249/664999923376193537
And another one from the time before that (thanks Isa) https://discordapp.com/channels/265929299490635777/412414009694748693/681612534171566103
Iteration after discussions on discord:
What plugins should be able to do:
To allow all these while still exposing a simple interface we will likely need multiple ways to add entries to a menu, allowing for very easy simple additions while still providing greater control with more complex alternatives.
The main code exposes a register for each expandable menu where plugins can add new entries. This register would be a templated class (due to the fact that the selection context type is different for different menus), that exposes register()
functions to plugins.
A mock example:
MenuRegister<std::Vector<ModInfo>> modlistMenu;
m_OrganizerCore->PluginContributionPoints->modlistMenu->registerEntry(name, triggeredCallback, showCallback);
triggeredCallback
and showCallback
would take a vector of ModInfo
s representing the currently selected mods on the modlist.
A variation of registerEntry
could take a callback for the name as well to allow context aware naming of the entry.
Consecutive calls to registerEntry
will remember entry order.
Something like registerSeparator()
could be used to add separators in.
For adding submenus another function would be needed but this time the plugin author needs to pass an entire structure of entries and their callbacks. To allow this we need to expose a structure type to plugins that they can use to generate their desidered entries and pass them back.
Using such a structure would allow late entry generation. Instead of registering the entries immediately the plugins could just register their interest in adding entries later so basically just a callback that returns a generated structure of entries to add based on the selection context. This callback will be executed by the menu register when the menu is requested by the user.
On the mo2 side in a OnContextMenuRequested()
all that would be needed to do is to query the respective menuRegister
passing the current context to get back the entries to add. The register will be the one that executes all the show()
callbacks to test whether the entries are valid and potentially requests plugins to return a custom entry structure based on context.
With the proposed method plugins can't decide where in the menu their new entries will end up and they can't extend submenus of other plugins since the QMenu object isn't passed around.
My idea was to simply have the mo2 dev decide a sensible place in the menu where to put potential addition and have all the plugin added entries relegated to that place. Entries from the same plugin would be sequential and keep order, but the relative order of additions from different plugins would not be adjustable.
For concerns about menus getting too long due to plugins adding too many options, the Mo2 developer could set a limit to the number of entries that can stay at top level and move them under an "> Others..." submenu in case there are too many of them. The number would depend on the menu in question.
Another thing to define is the structure of late added entries and submenus, that will need to be exposed to the python interface.
Problem:
Currently Mo2 plugins can't add context menu options to the main context menus present in Mo2. This severely limits their usability and functionality, reducing the interest in creating new plugins since they would result less useful.
Goals:
A system that can be easily added to all interested context menus once implemented that allows plugins to:
The usage of such a system should be made as easy as possible for the plugin authors.
The list of interesting context menus in order of probable usefulness: [ ] Modlist [ ] Pluginlist [ ] Donwloads list [ ] Data tab [ ] Mod Info Filetree [ ] Overwrite Filetree [ ] Saves tab [ ] Archives tab (if it survives) [ ] ... more as requested ...
Proposed solution:
System based on functions added to the MO2 interfaces that the plugins can use to register new options, for example:
void Modlist::registerContextMenuOptions(MenuStructure subMenuToAdd, QString pluginName?)
Immediate problem with this approach is that some of these lists might not be initialized during plugin initialization so instead we could have a class were we store the options that each modlist can then query. So maybe something like this:
void OrganizerCore::registerContextMenuOptions(MenuStructure menuToAdd, OrganizerCore::MenuTargetsEnum target, QString pluginName)
substitute OrganizerCore with a dedicated class if necessary.What data does MO2 need from the Plugins: Possibly some kind of way to identify which plugin added the menu.
A list or vector of "Elements" to add. Each element can be either
A menu group needs the following data:
A menu separator has no info needed.
For a menu option Mo2 requires
bool showOption(Selection sel)
void optionTriggered(Selection sel)
I don't actually know much about the python interface and how that works so I don't know how difficult or problematic creating a new structure such as the one described above could be.
Otherwise could the plugin simply return a QMenu object? Would that work?
The relative order of these elements should be preserved for the presentation.
The Plugins on the other hand require Mo2 to give data regarding the current context menu request, specifically the selected items (for multiple selection it would be useful to know which item is the one the user actually pressed on to still perform single target operations on that element at least). Most target lists for context menus (like the modlist) are already exposed in someway to the plugins so they would just require to know what is currently selected.
Limitations of proposed solution:
Notes
This is just what I was able to think about for now, there could be issues I have not considered yet so please discuss here of possible problems or alternatives of this proposition.