Skip to content

Commit

Permalink
PathListingWidget : Add column context menu signal
Browse files Browse the repository at this point in the history
This follows the pattern established by other context menus, in allowing multiple client slots to collaborate on the building of a context menu.

I debated whether or not the signal should pass the `path` that is currently under the mouse, but decided against. That would encourage the creation of menu items that operate only on that path, but with multiple selection I believe the user expectation would be that the entire selection is operated on. By forcing clients to go fetch the selection themselves they are more likely to do the expected thing with it.
  • Loading branch information
johnhaddon committed Aug 1, 2024
1 parent 73bf2b3 commit c0a07f4
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ Fixes
- WidgetAlgo : Fixed issue preventing `grab()` from capturing popup menus on Windows.
- ShowURL : Fixed opening of "file://" URLs on Windows (#5861).

API
---

- PathListingWidget : Added `columnContextMenuSignal()`, allowing multiple clients to collaborate on the creation of a column-specific context menu.

Documentation
-------------

Expand Down
56 changes: 56 additions & 0 deletions python/GafferUI/PathListingWidget.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def __init__(
self.__displayModeChangedSignal = GafferUI.WidgetSignal()
self.__expansionChangedSignal = GafferUI.WidgetSignal()
self.__updateFinishedSignal = GafferUI.WidgetSignal()
self.__columnContextMenuSignal = Gaffer.Signal3()

# Connections for implementing selection and drag and drop.
self.keyPressSignal().connect( Gaffer.WeakMethod( self.__keyPress ), scoped = False )
Expand All @@ -157,6 +158,7 @@ def __init__(
self.mouseMoveSignal().connect( Gaffer.WeakMethod( self.__mouseMove ), scoped = False )
self.dragBeginSignal().connect( Gaffer.WeakMethod( self.__dragBegin ), scoped = False )
self.dragEndSignal().connect( Gaffer.WeakMethod( self.__dragEnd ), scoped = False )
self.contextMenuSignal().connect( Gaffer.WeakMethod( self.__contextMenu ), scoped = False )
self.__dragPointer = "paths"

self.__path = None
Expand Down Expand Up @@ -423,6 +425,19 @@ def pathSelectedSignal( self ) :

return self.__pathSelectedSignal

## Signal emitted to generate a context menu for a column. This allows
# multiple clients to collaborate in the construction of a menu, with each
# providing different items. It should be preferred to the generic
# `Widget.contextMenuSignal()`.
#
# Slots must have the following signature, with the `menuDefinition` being
# edited directly in place :
#
# `slot( column, pathListingWidget, menuDefinition )`
def columnContextMenuSignal( self ) :

return self.__columnContextMenuSignal

def setDragPointer( self, dragPointer ) :

self.__dragPointer = dragPointer
Expand Down Expand Up @@ -749,6 +764,47 @@ def __dragEnd( self, widget, event ) :

GafferUI.Pointer.setCurrent( None )

def __contextMenu( self, widget ) :

if not self.columnContextMenuSignal().numSlots() :
# Allow legacy clients connected to `Widget.contextMenuSignal()` to
# do their own thing instead.
return False

# Select the path under the mouse, if it's not already selected.
# The user will expect to be operating on the thing under the mouse.

mousePosition = GafferUI.Widget.mousePosition( relativeTo = self )
column = self.columnAt( mousePosition )

path = self.pathAt( mousePosition )
if path is not None :
path = str( path )
selection = self.getSelection()
if isinstance( selection, IECore.PathMatcher ) :
# Row or Rows mode.
if not selection.match( path ) & IECore.PathMatcher.Result.ExactMatch :
selection = IECore.PathMatcher( [ path ] )
self.setSelection( selection )
else :
# Cell or Cells mode.
columnIndex = self.getColumns().index( column )
if not selection[columnIndex].match( path ) & IECore.PathMatcher.Result.ExactMatch :
for i in range( 0, len( selection ) ) :
selection[i] = IECore.PathMatcher() if columnIndex != i else IECore.PathMatcher( [ path ] )
self.setSelection( selection )

# Use signals to build menu and display it.

menuDefinition = IECore.MenuDefinition()
self.columnContextMenuSignal()( column, self, menuDefinition )

if menuDefinition.size() :
self.__columnContextMenu = GafferUI.Menu( menuDefinition )
self.__columnContextMenu.popup( self )

return True

def __indexAt( self, position ) :

point = self._qtWidget().viewport().mapFrom(
Expand Down

0 comments on commit c0a07f4

Please sign in to comment.