Allow registering window, panel and context menu items with callbacks

This commit is contained in:
baldurk
2018-10-22 16:50:51 +01:00
parent 67fc971cd9
commit 94dfb9890b
24 changed files with 963 additions and 67 deletions
+301 -1
View File
@@ -35,6 +35,7 @@
#include <QProgressDialog>
#include <QRegularExpression>
#include <QTimer>
#include "Code/Resources.h"
#include "Code/pyrenderdoc/PythonContext.h"
#include "Windows/APIInspector.h"
#include "Windows/BufferViewer.h"
@@ -348,10 +349,309 @@ bool CaptureContext::LoadExtension(rdcstr name)
}
});
m_MainWindow->CleanMenus();
for(QAction *a : m_MainWindow->GetMenuActions())
CleanMenu(a);
m_RegisteredMenuItems.removeAll(NULL);
return ret;
}
void CaptureContext::RegisterWindowMenu(WindowMenu base, const rdcarray<rdcstr> &submenus,
ExtensionCallback callback)
{
if(base == WindowMenu::Unknown)
{
qCritical() << "Can't register menu for WindowMenu::Unknown";
return;
}
if(submenus.isEmpty())
{
qCritical() << "Empty list of submenus";
return;
}
if(base == WindowMenu::NewMenu && submenus.count() == 1)
{
qCritical() << "Need at least two items in submenus for WindowMenu::NewMenu";
return;
}
QMenu *menu = m_MainWindow->GetBaseMenu(base, submenus[0]);
if(!menu)
{
qCritical() << "Couldn't get base menu";
return;
}
std::function<void()> slotcallback = [this, callback]() { callback(this, {}); };
// if it's a new menu, GetBaseMenu already did the work, so skip the 0th element of submenus
if(base == WindowMenu::NewMenu)
{
rdcarray<rdcstr> items = submenus;
items.erase(0);
AddSortedMenuItem(menu, false, items, slotcallback);
}
else
{
AddSortedMenuItem(menu, false, submenus, slotcallback);
}
}
void CaptureContext::RegisterPanelMenu(PanelMenu base, const rdcarray<rdcstr> &submenus,
ExtensionCallback callback)
{
if(base == PanelMenu::Unknown)
{
qCritical() << "Can't register menu for PanelMenu::Unknown";
return;
}
RegisteredMenuItem *item = new RegisteredMenuItem();
item->panel = base;
item->submenus = submenus;
item->callback = callback;
m_RegisteredMenuItems.push_back(item);
m_PendingExtensionObjects.push_back(item);
}
void CaptureContext::RegisterContextMenu(ContextMenu base, const rdcarray<rdcstr> &submenus,
ExtensionCallback callback)
{
if(base == ContextMenu::Unknown)
{
qCritical() << "Can't register menu for ContextMenu::Unknown";
return;
}
RegisteredMenuItem *item = new RegisteredMenuItem();
item->context = base;
item->submenus = submenus;
item->callback = callback;
m_RegisteredMenuItems.push_back(item);
m_PendingExtensionObjects.push_back(item);
}
void CaptureContext::MenuDisplaying(ContextMenu contextMenu, QMenu *menu,
const ExtensionCallbackData &data)
{
ContextMenu contextMenuAlt = contextMenu;
switch(contextMenu)
{
case ContextMenu::MeshPreview_GSOutVertex:
case ContextMenu::MeshPreview_VSInVertex:
case ContextMenu::MeshPreview_VSOutVertex:
contextMenuAlt = ContextMenu::MeshPreview_Vertex;
break;
case ContextMenu::TextureViewer_InputThumbnail:
case ContextMenu::TextureViewer_OutputThumbnail:
contextMenuAlt = ContextMenu::TextureViewer_Thumbnail;
break;
}
for(RegisteredMenuItem *item : m_RegisteredMenuItems)
{
if(item->context == contextMenu || item->context == contextMenuAlt)
{
AddSortedMenuItem(menu, true, item->submenus, [this, item, data]() {
rdcarray<rdcpair<rdcstr, PyObject *>> args;
PythonContext::ConvertPyArgs(data, args);
item->callback(this, args);
PythonContext::FreePyArgs(args);
});
}
}
}
void CaptureContext::MenuDisplaying(PanelMenu panelMenu, QWidget *extensionButton,
const ExtensionCallbackData &data)
{
QMenu contextMenu(m_MainWindow);
for(RegisteredMenuItem *item : m_RegisteredMenuItems)
{
if(item->panel == panelMenu)
{
AddSortedMenuItem(&contextMenu, false, item->submenus, [this, item, data]() {
rdcarray<rdcpair<rdcstr, PyObject *>> args;
PythonContext::ConvertPyArgs(data, args);
item->callback(this, args);
PythonContext::FreePyArgs(args);
});
}
}
QAction emptyAction(tr("No extension commands configured"), m_MainWindow);
if(contextMenu.isEmpty())
{
contextMenu.addAction(&emptyAction);
QObject::connect(&emptyAction, &QAction::triggered,
[this]() { m_MainWindow->showExtensionManager(); });
}
RDDialog::show(&contextMenu, extensionButton->mapToGlobal(extensionButton->rect().bottomLeft()));
}
void CaptureContext::AddSortedMenuItem(QMenu *menu, bool rootMenu, const rdcarray<rdcstr> &items,
std::function<void()> callback)
{
QAction *beforeAction = NULL;
bool addIcon = rootMenu;
// if we're inserting into a pre-existing menu, try to find an __examples__ action which tells us
// where
// to be inserting
{
QList<QAction *> children = menu->actions();
int idx = 0;
for(; idx < children.count(); idx++)
{
if(children[idx]->text() == lit("__extensions__"))
break;
}
// if we found it, set beforeAction
if(idx < children.count())
{
// search back to find the right place alphabetically to place this, if there are existing
// menus
for(; idx > 0;)
{
// if the previous child is a separator, break, there are no more previous menus
if(children[idx - 1]->isSeparator())
break;
// if we should be sorted before the previous child, move backwards
if(children[idx - 1]->text() > items[0])
{
idx--;
continue;
}
// if we found the right place, break
break;
}
beforeAction = children[idx];
addIcon = true;
}
}
for(int i = 0; i < items.count() - 1; i++)
{
QList<QAction *> children = menu->actions();
bool found = false;
// see if this submenu already exists, if so just iterate down to it
for(QAction *a : children)
{
if(a->text() == items[i])
{
menu = a->menu();
found = true;
break;
}
}
if(found)
{
addIcon = false;
beforeAction = NULL;
continue;
}
// create the new submenu
QMenu *submenu = new QMenu(items[i], m_MainWindow);
// if we don't have a beforeAction, find where to insert this to keep alphabetical order.
if(!beforeAction)
{
for(QAction *a : children)
{
if(submenu->menuAction()->text() < a->text())
{
beforeAction = a;
break;
}
}
}
if(beforeAction)
menu->insertMenu(beforeAction, submenu);
else
menu->addMenu(submenu);
if(addIcon)
submenu->setIcon(Icons::plugin());
addIcon = false;
beforeAction = NULL;
menu = submenu;
}
QAction *action = new QAction(m_MainWindow);
action->setText(items.back());
QObject::connect(action, &QAction::triggered, callback);
if(!beforeAction)
{
QList<QAction *> children = menu->actions();
for(QAction *a : children)
{
if(action->text() < a->text())
{
beforeAction = a;
break;
}
}
}
if(beforeAction)
menu->insertAction(beforeAction, action);
else
menu->addAction(action);
if(addIcon)
action->setIcon(Icons::plugin());
m_PendingExtensionObjects.push_back(action);
}
void CaptureContext::CleanMenu(QAction *action)
{
QMenu *menu = action->menu();
// if this isn't a menu, nothing to do - return
if(!menu)
return;
// first clean all our children
for(QAction *a : menu->actions())
CleanMenu(a);
// if we no longer have any children in this menu, delete it.
if(menu->actions().isEmpty())
delete menu;
}
void CaptureContext::LoadCapture(const rdcstr &captureFile, const rdcstr &origFilename,
bool temporary, bool local)
{