Skip to content

Conversation

@jhaigh0
Copy link
Contributor

@jhaigh0 jhaigh0 commented Jan 7, 2026

Description of work

Previously, when selecting multiple workspaces and right-clicking, the context menu shown would be for the workspace which the mouse was over. However when selecting an option, all workspace names would be passed to that process. This could lead to problems like the one reported by a user in the original issue.

In this PR the system for generating the context menu is reworked so that only the actions which are available for all selected workspaces are added. This should stop any similar bugs.

Closes #40401

To test:

Create a few different kinds of workspaces e.g

  • Some normal matrix workspace data
  • A matrix workspace without an instrument (can use RemoveInstrumentGeometry)
  • A table workspace
  • An MD workspace
  • A workspace group

Select different combinations of these and check that the options present in the context menu make sense (are the ones which are shared between them individually)
Also select multiple workspaces of the same type and check that everything is displayed normally, plus any other tests you can think of.


Reviewer

Your comments will be used as part of the gatekeeper process. Comment clearly on what you have checked and tested during your review. Provide an audit trail for any changes requested.

As per the review guidelines:

  • Is the code of an acceptable quality? (Code standards/GUI standards)
  • Has a thorough functional test been performed? Do the changes handle unexpected input/situations?
  • Are appropriately scoped unit and/or system tests provided?
  • Do the release notes conform to the guidelines and describe the changes appropriately?
  • Has the relevant (user and developer) documentation been added/updated?
  • If the PR author isn’t in the mantid-developers or mantid-contributors teams, add a review comment rerun ci to authorize/rerun the CI

Gatekeeper

As per the gatekeeping guidelines:

  • Has a thorough first line review been conducted, including functional testing?
  • At a high-level, is the code quality sufficient?
  • Are the base, milestone and labels correct?

@github-actions github-actions bot added this to the Release 6.15 milestone Jan 7, 2026
@jhaigh0 jhaigh0 changed the title 40401 disable menu options for multiple workspace types Take intersection of context menu options when selecting multiple workspaces Jan 7, 2026
@jhaigh0
Copy link
Contributor Author

jhaigh0 commented Jan 7, 2026

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

✅ Actions performed

Full review triggered.

1 similar comment
@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

The change refactors workspace context-menu generation to support multiple selected workspaces. It adds a new Mantid::API::plotMenuActions struct and a QAction* m_separator. createWorkspaceContextMenu now accepts a QStringList of selections. Per-workspace builders were changed to return action vectors and plotMenuActions (for Matrix, MD, and groups) or vectors (for Table), and a new intersectionOfActions computes common actions. Menu construction now collects per-item actions, intersects them, and only inserts Plot and 3D submenus when common plot actions exist.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Take intersection of context menu options when selecting multiple workspaces' directly and clearly describes the main purpose of the changeset: computing and displaying only common actions across multiple selected workspaces.
Linked Issues check ✅ Passed The PR successfully implements the core requirement from #40401: showing only intersection of available actions when multiple workspaces are selected, preventing errors from attempting unsupported operations on incompatible workspace types.
Out of Scope Changes check ✅ Passed All changes are scoped to WorkspaceTreeWidgetSimple header and implementation files, introducing multi-workspace context menu logic and action intersection functionality directly addressing the linked issue requirements.
Description check ✅ Passed The PR description clearly explains the problem (context menu showing options for only the mouse-over workspace while applying to all selected ones) and the solution (showing only intersecting actions across all selected workspaces), directly relating to the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp:
- Around line 128-129: There's a misspelled variable name: change
selectedWorksapceNames to selectedWorkspaceNames wherever it's declared and
used; specifically update the assignment from getSelectedWorkspaceNamesAsQList()
and the subsequent call to createWorkspaceContextMenu(selectedWorkspaceNames)
(and any other references in the same scope) so the identifier matches
correctly.
- Around line 318-332: The function
WorkspaceTreeWidgetSimple::intersectionOfActions can dereference an empty
actionVecs via actionVecs.front(), causing UB; fix by checking if
actionVecs.empty() (and possibly if any inner vector is empty) at the top and
return an empty std::vector<QAction*> immediately, then proceed with
sorting/combining as before; ensure std::erase_if on combinedActions is only
performed when combinedActions was initialized from a non-empty front() to avoid
crashes.
- Around line 238-270: The bug is that workspaces.push_back(workspace) is called
before type checks, so unhandled workspace types create size mismatches with
actionVecs and can make intersectionOfActions crash; fix by moving the
workspaces.push_back(workspace) into each handled branch (inside the
MatrixWorkspace, ITableWorkspace, IMDWorkspace, and WorkspaceGroup branches) so
only workspaces that produce corresponding actionVecs are added, and remove the
earlier unconditional push (or alternatively add an else { continue; } to skip
unhandled types).
🧹 Nitpick comments (3)
qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (2)

22-25: Move plotMenuActions struct inside the namespace.

The struct is defined outside MantidQt::MantidWidgets namespace, which pollutes the global namespace. Since it's only used by WorkspaceTreeWidgetSimple, it should be defined within the same namespace for consistency and encapsulation.

♻️ Suggested refactor
-struct plotMenuActions {
-  std::vector<QAction *> plotActions = {};
-  std::vector<QAction *> plot3DActions = {};
-};
-
 namespace MantidQt::MantidWidgets {
+
+struct plotMenuActions {
+  std::vector<QAction *> plotActions = {};
+  std::vector<QAction *> plot3DActions = {};
+};
+
 class MantidDisplayBase;

111-111: Use const reference for input parameter.

selectedWorkspaces is not modified by this function, so it should be passed as const QStringList & for const-correctness.

♻️ Suggested fix
-  QMenu *createWorkspaceContextMenu(QStringList &selectedWorkspaces);
+  QMenu *createWorkspaceContextMenu(const QStringList &selectedWorkspaces);
qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (1)

233-233: Use const reference for input parameter (consistency with header fix).

The parameter should be const QStringList & to match the suggested header change and ensure const-correctness.

♻️ Suggested fix
-QMenu *WorkspaceTreeWidgetSimple::createWorkspaceContextMenu(QStringList &selectedWorkspaces) {
+QMenu *WorkspaceTreeWidgetSimple::createWorkspaceContextMenu(const QStringList &selectedWorkspaces) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1d2cb2 and 9f8d73d.

📒 Files selected for processing (2)
  • qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h
  • qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,cc,cxx,c++,h,hpp,hxx}

📄 CodeRabbit inference engine (AGENTS.md)

Run clang-tidy for code linting using the configuration in .clang-tidy

Files:

  • qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h
  • qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
🧬 Code graph analysis (1)
qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (1)
qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (8)
  • createWorkspaceContextMenu (233-316)
  • createWorkspaceContextMenu (233-233)
  • createMatrixWorkspacePlotMenu (448-473)
  • createMatrixWorkspacePlotMenu (448-448)
  • hasMultipleBins (33-45)
  • hasMultipleBins (33-33)
  • intersectionOfActions (318-332)
  • intersectionOfActions (319-319)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: doxygen
🔇 Additional comments (6)
qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (1)

130-131: LGTM on the new m_separator member.

Using a dedicated QAction as a reusable separator is a reasonable approach for building dynamic menus.

qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (5)

69-69: LGTM on separator initialization.

The m_separator action is properly initialized and configured as a separator in the constructor.

Also applies to: 104-104


276-291: Instrument view enablement logic looks correct.

The code properly checks if m_showInstrument is in the combined actions before computing the enabled state across all workspaces. Using std::all_of ensures the instrument view is only enabled when all selected workspaces support it.


293-310: Plot submenu construction is well-structured.

The conditional building of Plot and 3D submenus only when actions are available is clean. The menu hierarchy (Plot → 3D) is properly established.


351-354: Single-selection check within per-workspace function.

This check for m_tree->selectedItems().size() == 1 correctly restricts sample material/shape actions to single-selection scenarios. Since this is called for each workspace but the check is global, the actions will only be added when exactly one item is selected.


448-473: LGTM on refactored plot menu creation.

The function now returns a plotMenuActions struct instead of a QMenu*, which aligns with the new action-vector-based approach for building menus from intersections.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp:
- Around line 318-332: The function
WorkspaceTreeWidgetSimple::intersectionOfActions must guard against an empty
actionVecs to avoid calling actionVecs.front(); add an early check returning an
empty std::vector<QAction*> when actionVecs.empty(), then proceed with the
existing sort/combinedActions logic (using actionVecs.front() and std::erase_if)
for non-empty input; ensure you update only the body of intersectionOfActions
and keep the rest of the algorithm unchanged.
- Around line 128-129: There's a typo in the local variable name: change
selectedWorksapceNames to selectedWorkspaceNames where it's declared and any
subsequent uses (e.g., the call to getSelectedWorkspaceNamesAsQList() and the
argument passed into createWorkspaceContextMenu); update both the variable
declaration and its usage to the correctly spelled selectedWorkspaceNames to
avoid undefined symbol/compile errors.
- Around line 238-270: The loop currently always pushes the retrieved workspace
into workspaces but only appends to actionVecs for certain types, causing a
potential size mismatch and a crash in intersectionOfActions; fix by ensuring
workspaces and actionVecs stay in sync—either move the
workspaces.push_back(workspace) into each handled branch (the branches using
std::dynamic_pointer_cast<MatrixWorkspace>, ITableWorkspace, IMDWorkspace,
WorkspaceGroup that call
createMatrixWorkspaceActions/createTableWorkspaceActions/createMDWorkspaceActions/createWorkspaceGroupActions)
so you only add when you also add an actionVec, or skip/continue when no branch
matches (i.e., do not push the workspace for unhandled types), and additionally
add a guard after the loop to return/disable further processing if actionVecs is
empty before calling intersectionOfActions.
🧹 Nitpick comments (2)
qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (1)

276-291: Cache getInstrument() result to avoid redundant calls.

Lines 279-280 call workspace->getInstrument() twice. Caching the result would improve efficiency.

♻️ Suggested refactor
     const auto showInstrumentViewCheck = [](MatrixWorkspace_const_sptr workspace) {
-      return workspace->getInstrument() && !workspace->getInstrument()->getName().empty() &&
-             workspace->getAxis(1)->isSpectra();
+      const auto instrument = workspace->getInstrument();
+      return instrument && !instrument->getName().empty() && workspace->getAxis(1)->isSpectra();
     };
qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (1)

22-25: Consider moving struct into MantidQt::MantidWidgets namespace.

The plotMenuActions struct is currently in the global namespace. For better encapsulation and to avoid namespace pollution, consider moving it into MantidQt::MantidWidgets.

♻️ Suggested change
 namespace Mantid::API {
 class Workspace;
 }
 
-struct plotMenuActions {
-  std::vector<QAction *> plotActions = {};
-  std::vector<QAction *> plot3DActions = {};
-};
-
 namespace MantidQt::MantidWidgets {
+
+struct plotMenuActions {
+  std::vector<QAction *> plotActions = {};
+  std::vector<QAction *> plot3DActions = {};
+};
+
 class MantidDisplayBase;

This would also require updating the return types in the function declarations to use the fully qualified name or bringing the struct into scope.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1d2cb2 and 9f8d73d.

📒 Files selected for processing (2)
  • qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h
  • qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,cc,cxx,c++,h,hpp,hxx}

📄 CodeRabbit inference engine (AGENTS.md)

Run clang-tidy for code linting using the configuration in .clang-tidy

Files:

  • qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h
  • qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
🧠 Learnings (1)
📚 Learning: 2025-10-22T13:08:14.586Z
Learnt from: GuiMacielPereira
Repo: mantidproject/mantid PR: 40194
File: Framework/Algorithms/src/CreateDetectorTable.cpp:64-68
Timestamp: 2025-10-22T13:08:14.586Z
Learning: In Mantid Framework, MatrixWorkspace::getInstrument() (inherited from ExperimentInfo) throws an exception if no instrument is found rather than returning nullptr. The method dereferences the internal instrument pointer, so null-checking the return value of getInstrument() is unnecessary. Only check the objects returned by methods called on the instrument (e.g., getSample()).

Applied to files:

  • qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp
🧬 Code graph analysis (1)
qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (1)
qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (2)
  • hasMultipleBins (33-45)
  • hasMultipleBins (33-33)
🔇 Additional comments (3)
qt/widgets/common/src/WorkspacePresenter/WorkspaceTreeWidgetSimple.cpp (2)

69-69: LGTM: Separator initialization and usage.

The separator action is correctly initialized and configured. Qt will manage its lifetime through the parent-child relationship.

Also applies to: 104-104


334-357: Well-structured refactoring to support action intersection.

The refactored action creator functions correctly return action vectors/tuples instead of directly mutating menus. This enables the intersection logic to compute common actions across multiple selected workspaces, directly addressing the PR objective. Each workspace type appropriately returns its supported actions:

  • MatrixWorkspace: Full plot menu for multi-bin data, limited for single values
  • TableWorkspace: No plot actions (correct, as tables aren't plottable)
  • MDWorkspace: Conditional slice viewer or 1D plot based on dimensions
  • WorkspaceGroup: Actions derived from contained workspace types

Also applies to: 359-368, 370-407, 409-438, 448-473

qt/widgets/common/inc/MantidQtWidgets/Common/WorkspacePresenter/WorkspaceTreeWidgetSimple.h (1)

111-124: LGTM: Header declarations match implementation.

The updated function signatures correctly reflect the refactored implementation:

  • createWorkspaceContextMenu now accepts multiple workspace names
  • Action creator functions return vectors/tuples for intersection computation
  • New intersectionOfActions helper enables common action calculation
  • m_separator member supports clean action list formatting

Also applies to: 130-131

@jhaigh0 jhaigh0 added ISIS: Core Issue and pull requests at ISIS that relate to Core features and functions Reported By User Issues that were found or highlighted by a user/scientist GUI Issues and pull requests specific to the Mantid Workbench GUI. labels Jan 7, 2026
@jhaigh0 jhaigh0 marked this pull request as ready for review January 7, 2026 14:23
Copy link
Contributor

@adriazalvarez adriazalvarez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for implementing this. I've been trying with a few different combinations, everything seems to be working well, and the bug is not present.
I have added some comments in the code, but here are a few general suggestions:

  • Release notes are missing.
  • Since we are in this part of the code, could be add a small paragraph on the context menu docs with the dynamic behaviour of the context menu (workspacetreewidget.rst)
  • Is it feasable to use cpp implementations of sets (std::sets) or function like std::set_intersection to find the intersections? Perhaps would simplify the code.


const auto it = std::find(combinedActions.cbegin(), combinedActions.cend(), m_showInstrument);
if (it != combinedActions.cend()) {
const auto showInstrumentViewCheck = [](MatrixWorkspace_const_sptr workspace) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this lambda just be plug in on L284?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just did this for readability, it got too confusing when it was all one statement I think.

jhaigh0 and others added 2 commits January 8, 2026 15:13
Co-authored-by: Adri Diaz <146007827+adriazalvarez@users.noreply.github.com>
@jhaigh0 jhaigh0 force-pushed the 40401_disable_menu_options_for_multiple_workspace_types branch from acd500e to 381a4d7 Compare January 8, 2026 15:20
@jhaigh0
Copy link
Contributor Author

jhaigh0 commented Jan 8, 2026

Is it feasable to use cpp implementations of sets (std::sets) or function like std::set_intersection to find the intersections? Perhaps would simplify the code.

This is what I tried first. The main problem is that we want to preserve the order which the actions are listed in which isn't great for sets and also std::set_intersection wanted the vectors to be sorted first. The other is that there can be multiple separators in the list (so you'll lose on if you at them all to a set).
In the end, taking the largest list and filtering it seemed the best solution for this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GUI Issues and pull requests specific to the Mantid Workbench GUI. ISIS: Core Issue and pull requests at ISIS that relate to Core features and functions Reported By User Issues that were found or highlighted by a user/scientist

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error when plotting multiple workspaces and one of them is unsupported (TableWorkspace)

2 participants