Core: allow multiple contexts per widget

This makes it possible to allow different sets of actions for a specific
widget depending on the defined contexts for that widget.

Fixes: QTCREATORBUG-30675
Fixes: QTCREATORBUG-30677
Change-Id: I408e0ae445b364d4f450ccdd2fbdfc81ece45015
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
David Schulz
2024-04-12 12:53:27 +02:00
parent 310d3cc041
commit ee8245d8aa
4 changed files with 61 additions and 46 deletions

View File

@@ -314,7 +314,7 @@ public:
QList<IContext *> m_activeContext; QList<IContext *> m_activeContext;
std::unordered_map<QWidget *, IContext *> m_contextWidgets; std::unordered_map<QWidget *, QList<IContext *>> m_contextWidgets;
ShortcutSettings *m_shortcutSettings = nullptr; ShortcutSettings *m_shortcutSettings = nullptr;
ToolSettings *m_toolSettings = nullptr; ToolSettings *m_toolSettings = nullptr;
@@ -836,16 +836,18 @@ QString ICore::versionString()
} }
/*! /*!
Returns the top level IContext of the current context, or \c nullptr if Returns a list IContexts for the current top level context widget, or an empty list if
there is none. there is none.
\sa updateAdditionalContexts() \sa updateAdditionalContexts()
\sa addContextObject() \sa addContextObject()
\sa {The Action Manager and Commands} \sa {The Action Manager and Commands}
*/ */
IContext *ICore::currentContextObject() QList<IContext *> ICore::currentContextObjects()
{ {
return d->m_activeContext.isEmpty() ? nullptr : d->m_activeContext.first(); if (d->m_activeContext.isEmpty())
return {};
return d->m_contextWidgets[d->m_activeContext.first()->widget()];
} }
/*! /*!
@@ -856,8 +858,7 @@ IContext *ICore::currentContextObject()
*/ */
QWidget *ICore::currentContextWidget() QWidget *ICore::currentContextWidget()
{ {
IContext *context = currentContextObject(); return d->m_activeContext.isEmpty() ? nullptr : d->m_activeContext.first()->widget();
return context ? context->widget() : nullptr;
} }
/*! /*!
@@ -1525,8 +1526,10 @@ void ICore::extensionsInitialized()
void ICore::aboutToShutdown() void ICore::aboutToShutdown()
{ {
disconnect(qApp, &QApplication::focusChanged, d, &ICorePrivate::updateFocusWidget); disconnect(qApp, &QApplication::focusChanged, d, &ICorePrivate::updateFocusWidget);
for (auto contextPair : d->m_contextWidgets) for (auto contextsPair : d->m_contextWidgets) {
disconnect(contextPair.second, &QObject::destroyed, d->m_mainwindow, nullptr); for (auto context : contextsPair.second)
disconnect(context, &QObject::destroyed, d->m_mainwindow, nullptr);
}
d->m_activeContext.clear(); d->m_activeContext.clear();
d->m_mainwindow->hide(); d->m_mainwindow->hide();
} }
@@ -2281,13 +2284,14 @@ void ICore::openFileWith()
} }
/*! /*!
Returns the registered IContext instance for the specified \a widget, Returns all registered IContext instance for the specified \a widget,
if any. if any.
*/ */
IContext *ICore::contextObject(QWidget *widget) QList<IContext *> ICore::contextObjects(QWidget *widget)
{ {
const auto it = d->m_contextWidgets.find(widget); if (auto it = d->m_contextWidgets.find(widget); it != d->m_contextWidgets.end())
return it == d->m_contextWidgets.end() ? nullptr : it->second; return it->second;
return {};
} }
/*! /*!
@@ -2306,11 +2310,7 @@ void ICore::addContextObject(IContext *context)
{ {
if (!context) if (!context)
return; return;
QWidget *widget = context->widget(); d->m_contextWidgets[context->widget()].append(context);
if (d->m_contextWidgets.find(widget) != d->m_contextWidgets.end())
return;
d->m_contextWidgets.insert({widget, context});
connect(context, &QObject::destroyed, m_core, [context] { removeContextObject(context); }); connect(context, &QObject::destroyed, m_core, [context] { removeContextObject(context); });
} }
@@ -2331,15 +2331,19 @@ void ICore::removeContextObject(IContext *context)
disconnect(context, &QObject::destroyed, m_core, nullptr); disconnect(context, &QObject::destroyed, m_core, nullptr);
const auto it = std::find_if(d->m_contextWidgets.cbegin(), auto it = std::find_if(
d->m_contextWidgets.cend(), d->m_contextWidgets.begin(),
[context](const std::pair<QWidget *, IContext *> &v) { d->m_contextWidgets.end(),
return v.second == context; [context](const std::pair<QWidget *, QList<IContext *>> &v) {
return v.second.contains(context);
}); });
if (it == d->m_contextWidgets.cend()) if (it == d->m_contextWidgets.end())
return; return;
it->second.removeAll(context);
if (it->second.isEmpty())
d->m_contextWidgets.erase(it); d->m_contextWidgets.erase(it);
if (d->m_activeContext.removeAll(context) > 0) if (d->m_activeContext.removeAll(context) > 0)
d->updateContextObject(d->m_activeContext); d->updateContextObject(d->m_activeContext);
} }
@@ -2355,15 +2359,8 @@ void ICorePrivate::updateFocusWidget(QWidget *old, QWidget *now)
return; return;
QList<IContext *> newContext; QList<IContext *> newContext;
if (QWidget *p = QApplication::focusWidget()) { for (QWidget *p = QApplication::focusWidget(); p; p = p->parentWidget())
IContext *context = nullptr; newContext.append(ICore::contextObjects(p));
while (p) {
context = ICore::contextObject(p);
if (context)
newContext.append(context);
p = p->parentWidget();
}
}
// ignore toplevels that define no context, like popups without parent // ignore toplevels that define no context, like popups without parent
if (!newContext.isEmpty() || QApplication::focusWidget() == m_mainwindow->focusWidget()) if (!newContext.isEmpty() || QApplication::focusWidget() == m_mainwindow->focusWidget())

View File

@@ -87,9 +87,9 @@ public:
static void raiseWindow(QWidget *widget); static void raiseWindow(QWidget *widget);
static void raiseMainWindow(); static void raiseMainWindow();
static IContext *currentContextObject(); static QList<IContext *> currentContextObjects();
static QWidget *currentContextWidget(); static QWidget *currentContextWidget();
static IContext *contextObject(QWidget *widget); static QList<IContext *> contextObjects(QWidget *widget);
static void updateAdditionalContexts(const Context &remove, const Context &add, static void updateAdditionalContexts(const Context &remove, const Context &add,
ContextPriority priority = ContextPriority::Low); ContextPriority priority = ContextPriority::Low);
static void addAdditionalContext(const Context &context, static void addAdditionalContext(const Context &context,

View File

@@ -156,13 +156,9 @@ StatusBarContext::StatusBarContext(QObject *parent)
Context StatusBarContext::context() const Context StatusBarContext::context() const
{ {
IMode *currentMode = ModeManager::currentMode(); if (IMode *currentMode = ModeManager::currentMode())
QWidget *modeWidget = currentMode ? currentMode->widget() : nullptr; return currentMode->context();
if (modeWidget) { return {};
if (IContext *context = ICore::contextObject(modeWidget))
return context->context();
}
return Context();
} }
} // Core } // Core

View File

@@ -102,6 +102,7 @@ public:
void modeChanged(Id mode, Id old); void modeChanged(Id mode, Id old);
void requestContextHelp(); void requestContextHelp();
void requestContextHelpFor(QList<QPointer<IContext>> contexts);
void showContextHelp(const HelpItem &contextHelp); void showContextHelp(const HelpItem &contextHelp);
void activateIndex(); void activateIndex();
void activateContents(); void activateContents();
@@ -464,11 +465,32 @@ void HelpPluginPrivate::requestContextHelp()
const HelpItem tipHelp = tipHelpValue.canConvert<HelpItem>() const HelpItem tipHelp = tipHelpValue.canConvert<HelpItem>()
? tipHelpValue.value<HelpItem>() ? tipHelpValue.value<HelpItem>()
: HelpItem(tipHelpValue.toString()); : HelpItem(tipHelpValue.toString());
IContext *context = ICore::currentContextObject(); const QList<IContext *> contexts = ICore::currentContextObjects();
if (tipHelp.isEmpty() && context) if (contexts.isEmpty() && !tipHelp.isEmpty()) {
context->contextHelp([this](const HelpItem &item) { showContextHelp(item); });
else
showContextHelp(tipHelp); showContextHelp(tipHelp);
} else {
requestContextHelpFor(Utils::transform(contexts, [](IContext *context) {
return QPointer<IContext>(context);
}));
}
}
void HelpPluginPrivate::requestContextHelpFor(QList<QPointer<IContext>> contexts)
{
if (contexts.isEmpty())
return;
QPointer<IContext> context = contexts.takeFirst();
while (!context) {
if (contexts.isEmpty())
return;
context = contexts.takeFirst();
}
context->contextHelp([contexts, this](const HelpItem &item) {
if (!item.isEmpty())
showContextHelp(item);
else
requestContextHelpFor(contexts);
});
} }
void HelpPluginPrivate::showContextHelp(const HelpItem &contextHelp) void HelpPluginPrivate::showContextHelp(const HelpItem &contextHelp)