diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index b5539a62c..e429a46f2 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -93,13 +93,40 @@ struct CaptureSettings DECLARE_REFLECTION_STRUCT(CaptureSettings); -DOCUMENT("The main parent window of the application."); +DOCUMENT(R"(The main parent window of the application. + +.. function:: ShortcutCallback() + + Not a member function - the signature for any ``ShortcutCallback`` callbacks. +)"); struct IMainWindow { + typedef std::function ShortcutCallback; + DOCUMENT( "Retrieves the QWidget for this :class:`MainWindow` if PySide2 is available, or ``None``."); virtual QWidget *Widget() = 0; + DOCUMENT(R"(Register a callback for a particular key shortcut. + +This creates a managed shortcut. Qt's shortcut system doesn't allow specialisation/duplication, so +you can't use ``Ctrl+S`` for a shortcut in a window to update some changes if there's also a global +``Ctrl+S`` shortcut on the window. In the end, neither shortcut will be called. + +Instead this function allows the main window to manage shortcuts internally, and it will pick the +closest shortcut to a given action. The search goes from the widget with the focus currently up the +chain of parents, with the first match being used. If no matches are found, then a 'global' default +will be invoked, if it exists. + +:param str shortcut: The text string representing the shortcut, e.g. 'Ctrl+S'. +:param QWidget widget: A handle to the widget to use as the context for this shortcut, or ``None`` + for a global shortcut. Note that if an existing global shortcut exists the new one will not be + registered. +:rtype: ``str`` +)"); + virtual void RegisterShortcut(const QString &shortcut, QWidget *widget, + ShortcutCallback callback) = 0; + protected: IMainWindow() = default; ~IMainWindow() = default; diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 4b24f0775..30521ed23 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -134,19 +134,20 @@ EventBrowser::EventBrowser(ICaptureContext &ctx, QWidget *parent) }; for(int i = 0; i < 10; i++) { - QShortcut *sc = new QShortcut(QKeySequence(keys[i] | Qt::ControlModifier), this); - QObject::connect(sc, &QShortcut::activated, [this, i]() { jumpToBookmark(i); }); + ctx.GetMainWindow()->RegisterShortcut(QKeySequence(keys[i] | Qt::ControlModifier).toString(), + NULL, [this, i]() { jumpToBookmark(i); }); } - { - QShortcut *sc = new QShortcut(QKeySequence(Qt::Key_Left | Qt::ControlModifier), this); - QObject::connect(sc, &QShortcut::activated, this, &EventBrowser::on_stepPrev_clicked); - } + ctx.GetMainWindow()->RegisterShortcut(QKeySequence(Qt::Key_Left | Qt::ControlModifier).toString(), + NULL, [this]() { on_stepPrev_clicked(); }); - { - QShortcut *sc = new QShortcut(QKeySequence(Qt::Key_Right | Qt::ControlModifier), this); - QObject::connect(sc, &QShortcut::activated, this, &EventBrowser::on_stepNext_clicked); - } + ctx.GetMainWindow()->RegisterShortcut(QKeySequence(Qt::Key_Right | Qt::ControlModifier).toString(), + NULL, [this]() { on_stepNext_clicked(); }); + + ctx.GetMainWindow()->RegisterShortcut(QKeySequence(Qt::Key_Escape).toString(), ui->findStrip, + [this]() { on_HideFindJump(); }); + ctx.GetMainWindow()->RegisterShortcut(QKeySequence(Qt::Key_Escape).toString(), ui->jumpStrip, + [this]() { on_HideFindJump(); }); ui->events->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect(ui->events, &RDTreeWidget::customContextMenuRequested, this, diff --git a/qrenderdoc/Windows/EventBrowser.ui b/qrenderdoc/Windows/EventBrowser.ui index 085a57e14..475f3cb82 100644 --- a/qrenderdoc/Windows/EventBrowser.ui +++ b/qrenderdoc/Windows/EventBrowser.ui @@ -323,9 +323,6 @@ :/cross.png:/cross.png - - Esc - true @@ -436,9 +433,6 @@ :/cross.png:/cross.png - - Esc - true diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index 01796bcee..d4028ea9a 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -57,6 +57,8 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai { ui->setupUi(this); + installEventFilter(this); + setAcceptDrops(true); QObject::connect(ui->action_Load_Default_Layout, &QAction::triggered, this, @@ -166,8 +168,6 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai ui->action_Resolve_Symbols->setEnabled(false); ui->action_Resolve_Symbols->setText(tr("Resolve Symbols")); - bool loaded = LoadLayout(0); - LambdaThread *th = new LambdaThread([this]() { m_Ctx.Config().AddAndroidHosts(); for(RemoteHost *host : m_Ctx.Config().RemoteHosts) @@ -176,46 +176,6 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai th->selfDelete(true); th->start(); - // create default layout if layout failed to load - if(!loaded) - { - QWidget *eventBrowser = m_Ctx.GetEventBrowser()->Widget(); - - ui->toolWindowManager->addToolWindow(eventBrowser, ToolWindowManager::EmptySpace); - - QWidget *textureViewer = m_Ctx.GetTextureViewer()->Widget(); - - ui->toolWindowManager->addToolWindow( - textureViewer, - ToolWindowManager::AreaReference(ToolWindowManager::RightOf, - ui->toolWindowManager->areaOf(eventBrowser), 0.75f)); - - QWidget *pipe = m_Ctx.GetPipelineViewer()->Widget(); - - ui->toolWindowManager->addToolWindow( - pipe, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, - ui->toolWindowManager->areaOf(textureViewer))); - - QWidget *mesh = m_Ctx.GetMeshPreview()->Widget(); - - ui->toolWindowManager->addToolWindow( - mesh, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, - ui->toolWindowManager->areaOf(textureViewer))); - - QWidget *capDialog = m_Ctx.GetCaptureDialog()->Widget(); - - ui->toolWindowManager->addToolWindow( - capDialog, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, - ui->toolWindowManager->areaOf(textureViewer))); - - QWidget *apiInspector = m_Ctx.GetAPIInspector()->Widget(); - - ui->toolWindowManager->addToolWindow( - apiInspector, - ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, - ui->toolWindowManager->areaOf(eventBrowser), 0.3f)); - } - #if defined(Q_OS_WIN32) if(GetModuleHandleA("rdocself.dll")) { @@ -244,6 +204,27 @@ MainWindow::MainWindow(ICaptureContext &ctx) : QMainWindow(NULL), ui(new Ui::Mai #endif m_Ctx.AddLogViewer(this); + + QList actions = ui->menuBar->actions(); + + // register all the UI-designer created shortcut keys + for(int i = 0; i < actions.count(); i++) + { + QAction *a = actions[i]; + + QKeySequence ks = a->shortcut(); + if(!ks.isEmpty()) + { + m_GlobalShortcutCallbacks[ks] = [a]() { + if(a->isEnabled()) + a->trigger(); + }; + } + + // recurse into submenus by appending to the end of the list. + if(a->menu()) + actions.append(a->menu()->actions()); + } } MainWindow::~MainWindow() @@ -845,6 +826,53 @@ ToolWindowManager::AreaReference MainWindow::leftToolArea() return ToolWindowManager::AreaReference(ToolWindowManager::LastUsedArea); } +void MainWindow::show() +{ + bool loaded = LoadLayout(0); + + // create default layout if layout failed to load + if(!loaded) + { + QWidget *eventBrowser = m_Ctx.GetEventBrowser()->Widget(); + + ui->toolWindowManager->addToolWindow(eventBrowser, ToolWindowManager::EmptySpace); + + QWidget *textureViewer = m_Ctx.GetTextureViewer()->Widget(); + + ui->toolWindowManager->addToolWindow( + textureViewer, + ToolWindowManager::AreaReference(ToolWindowManager::RightOf, + ui->toolWindowManager->areaOf(eventBrowser), 0.75f)); + + QWidget *pipe = m_Ctx.GetPipelineViewer()->Widget(); + + ui->toolWindowManager->addToolWindow( + pipe, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->toolWindowManager->areaOf(textureViewer))); + + QWidget *mesh = m_Ctx.GetMeshPreview()->Widget(); + + ui->toolWindowManager->addToolWindow( + mesh, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->toolWindowManager->areaOf(textureViewer))); + + QWidget *capDialog = m_Ctx.GetCaptureDialog()->Widget(); + + ui->toolWindowManager->addToolWindow( + capDialog, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, + ui->toolWindowManager->areaOf(textureViewer))); + + QWidget *apiInspector = m_Ctx.GetAPIInspector()->Widget(); + + ui->toolWindowManager->addToolWindow( + apiInspector, + ToolWindowManager::AreaReference(ToolWindowManager::BottomOf, + ui->toolWindowManager->areaOf(eventBrowser), 0.3f)); + } + + QMainWindow::show(); +} + void MainWindow::recentLog(const QString &filename) { if(QFileInfo::exists(filename)) @@ -1258,6 +1286,71 @@ void MainWindow::OnEventChanged(uint32_t eventID) { } +void MainWindow::RegisterShortcut(const QString &shortcut, QWidget *widget, ShortcutCallback callback) +{ + QKeySequence ks = QKeySequence::fromString(shortcut); + + if(widget) + { + m_WidgetShortcutCallbacks[ks][widget] = callback; + } + else + { + if(m_GlobalShortcutCallbacks[ks]) + { + qCritical() << "Assigning duplicate global shortcut for" << ks; + return; + } + + m_GlobalShortcutCallbacks[ks] = callback; + } +} + +bool MainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if(event->type() == QEvent::ShortcutOverride) + { + QKeyEvent *ke = (QKeyEvent *)event; + + QKeySequence pressed(ke->modifiers() | ke->key()); + + // first see if there's a widget shortcut registered for this key. If so, check the focus + // hierarchy to see if we have any matches + QWidget *focus = QApplication::focusWidget(); + + if(focus && m_WidgetShortcutCallbacks.contains(pressed)) + { + const QMap callbacks = m_WidgetShortcutCallbacks[pressed]; + QList widgets = callbacks.keys(); + + while(focus) + { + // if we find a direct ancestor to the focus widget which is registered for this shortcut, + // then use that callback + if(widgets.contains(focus)) + { + callbacks[focus](); + event->accept(); + return true; + } + + // keep searching up the hierarchy + focus = focus->parentWidget(); + } + } + + // if we didn't find matches or no such shortcut is registered, try global shortcuts + if(m_GlobalShortcutCallbacks.contains(pressed)) + { + m_GlobalShortcutCallbacks[pressed](); + event->accept(); + return true; + } + } + + return QMainWindow::eventFilter(watched, event); +} + void MainWindow::on_action_Close_Log_triggered() { PromptCloseLog(); diff --git a/qrenderdoc/Windows/MainWindow.h b/qrenderdoc/Windows/MainWindow.h index f5f575755..a164aabd5 100644 --- a/qrenderdoc/Windows/MainWindow.h +++ b/qrenderdoc/Windows/MainWindow.h @@ -53,6 +53,7 @@ public: // IMainWindow QWidget *Widget() override { return this; } + void RegisterShortcut(const QString &shortcut, QWidget *widget, ShortcutCallback callback); // ILogViewerForm void OnLogfileLoaded() override; void OnLogfileClosed() override; @@ -63,6 +64,8 @@ public: ToolWindowManager::AreaReference mainToolArea(); ToolWindowManager::AreaReference leftToolArea(); + void show(); + void setProgress(float val); void takeLogOwnership() { m_OwnTempLog = true; } void LoadFromFilename(const QString &filename); @@ -133,6 +136,8 @@ private: void dragEnterEvent(QDragEnterEvent *event) override; void dropEvent(QDropEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + QString dragFilename(const QMimeData *mimeData); Ui::MainWindow *ui; @@ -140,6 +145,9 @@ private: QList m_LiveCaptures; + QMap m_GlobalShortcutCallbacks; + QMap> m_WidgetShortcutCallbacks; + RDLabel *statusIcon; RDLabel *statusText; QProgressBar *statusProgress; diff --git a/qrenderdoc/Windows/MainWindow.ui b/qrenderdoc/Windows/MainWindow.ui index 35fa1416b..7e658d6a9 100644 --- a/qrenderdoc/Windows/MainWindow.ui +++ b/qrenderdoc/Windows/MainWindow.ui @@ -199,9 +199,6 @@ E&xit - - Alt+F4 - diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index ea10788d1..1e4e01d31 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -217,9 +217,8 @@ void ShaderViewer::editShader(bool customShader, const QString &entryPoint, cons m_FindState = FindState(); }); - // TODO - shortcuts - QObject::connect(new QShortcut(QKeySequence(Qt::Key_S | Qt::ControlModifier), scintilla), - &QShortcut::activated, this, &ShaderViewer::on_save_clicked); + m_Ctx.GetMainWindow()->RegisterShortcut(QKeySequence(QKeySequence::Save).toString(), this, + [this]() { on_save_clicked(); }); QWidget *w = (QWidget *)scintilla; w->setProperty("filename", f);