Skip to content

Improve Event Delegation in qMRMLThreeDView and qMRMLSliceView #7310

@jcfr

Description

@jcfr

Is your feature request related to a problem? Please describe.

The current event delegation mechanism in Slicer is based on Slicer-specific interactor styles, namely vtkMRMLThreeDViewInteractorStyle and vtkMRMLSliceViewInteractorStyle, which are derived from vtkMRMLViewInteractorStyle and, ultimately, vtkInteractorStyle3D.

These styles handle interaction events by adding observers for events like EnterEvent, LeaveEvent, and MouseMoveEvent, and subsequently call functions like vtkMRMLViewInteractorStyle::OnEnter(), vtkMRMLViewInteractorStyle::OnLeave(), or vtkMRMLViewInteractorStyle::OnMouseMove().

In each of these On<EventName>() functions, delegation to displayable managers and their associated widgets is performed, and if a given event isn't intercepted, the default behavior for <EventName> implemented in vtkInteractorStyle3D is executed by calling this->Superclass::On<EventName>(). The snippet below illustrates this:

if (!this->DelegateInteractionEventToDisplayableManagers(vtkCommand::<EventName>Event))
  {
  return;
  }
this->Superclass::On<EventName>();

However, a limitation arises when default event handling implemented in specific interactor styles (e.g., vtkOpenXRInteractorStyle or vtkOpenVRInteractorStyle) needs to be executed when not delegated.

Describe the solution you'd like

Refactor Interactor Styles

Refactor vtkMRMLThreeDViewInteractorStyle and vtkMRMLSliceViewInteractorStyle to derive from vtkObject and implement event delegation to MRML displayable managers and associated widgets by observing the interactor style. Depending on the observed event, default interactor style behavior would be executed using one of the following approaches:

  1. Setting Abort Flag: For events such as Move3D, Button3D, Menu3D, and others where the vtkInteractorStyle class checks if the event was aborted, we will set an abort flag.
  2. Explicit Call of On<EventName>(): For other events, we would explicitly call the corresponding On<EventName>() function. To support this, the existing method vtkMRMLAbstractThreeDViewDisplayableManager::PassThroughInteractorStyleEvent will be used and updated as needed.

The interactor style will be associated with its corresponding displayable manager group, allowing access to the MRML scene and registered displayable managers.

Renaming Interactor Styles

To faciliate review of changes introduced in the first phase of the refactoring, the rename the classes will happen in in a second phase. This will help avoid future confusion.

More specifically, we will ename the classes vtkMRMLThreeDViewInteractorStyle to vtkMRMLThreeDViewInteractorObserver and vtkMRMLSliceViewInteractorStyle to vtkMRMLSliceViewInteractorObserver.

Access to CameraNode from vtkMRMLThreeDViewInteractorStyle

Access to the CameraNode will be updated in the following places:

  1. In qMRMLThreeDView, the code can be refactored to retrieve the camera node from the vtkMRMLCameraDisplayableManager by calling qMRMLThreeDView::displayableManagerByClassName().
  2. In vtkMRMLThreeDViewInteractorStyle, the camera node will be accessed through the displayable manager group following the refactoring described above.

Default Interactor Styles

  • For 3D views, the default interactor style will be vtkInteractorStyleTrackballCamera.
  • For 2D views, the default interactor style will be vtkInteractorStyleUser.

Whenever possible, Slicer-specific behavior will be added either to vtkMRMLThreeDViewInteractorStyle (renamed vtkMRMLThreeDViewInteractorObserver in Phase 2) or to displayable managers to ensure that switching the interactor style does not affect critical functionality.

Describe alternatives you've considered

An alternative would be duplicating the code currently implemented in VTK-specific interactor styles. However, this approach is error-prone and unsustainable.

Additional context

This proposal was created following discussion with @LucasGandel while discussing the following issue:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions