Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ListContainer alignment #5997

Merged
merged 2 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Improvements
- LightEditor, RenderPassEditor : History windows now use a context determined relative to the current focus node.
- NumericWidget : Added the ability to use <kbd>Ctrl</kbd> + scroll wheel to adjust values in the same manner as <kbd>Up</kbd> and <kbd>Down</kbd> (#6009). [^1]
- NodeEditor : Improved performance when showing a node with many colour plugs. Showing the Arnold `standard_surface` shader is now almost 2x faster. [^1]
- ListContainer : Adding a child widget with non-default alignment no longer causes the container to take up all available space.

Fixes
-----
Expand Down
8 changes: 2 additions & 6 deletions python/GafferUI/GraphEditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,7 @@ def __init__( self, scriptNode, **kw ) :
self.__gadgetWidget.getViewportGadget().preRenderSignal().connect( Gaffer.WeakMethod( self.__preRender ) )

with GafferUI.ListContainer( borderWidth = 8, spacing = 0 ) as overlay :
with GafferUI.ListContainer(
GafferUI.ListContainer.Orientation.Horizontal,
parenting = {
"verticalAlignment" : GafferUI.VerticalAlignment.Top,
}
) :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal ) :
GafferUI.Spacer( imath.V2i( 1 ) )
GafferUI.MenuButton(
image = "annotations.png", hasFrame = False,
Expand All @@ -88,6 +83,7 @@ def __init__( self, scriptNode, **kw ) :
title = "Annotations"
)
)
GafferUI.Spacer( imath.V2i( 1 ) )

self.__gadgetWidget.addOverlay( overlay )

Expand Down
45 changes: 43 additions & 2 deletions python/GafferUI/ListContainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,28 @@
from Qt import QtWidgets
from Qt import QtCore

class _WidgetItem( QtWidgets.QWidgetItem ) :
# A customized `QWidgetItem` to return a `maximumSize()` without taking
# into account its `alignment`. Qt's `QBoxLayout` returns `QLAYOUTSIZE_MAX`
# whenever there's an alignment set, resulting in unnecessarily greedy
# aligned layouts. We override `QWidgetItem` instead of `QBoxLayout`
# because in `QBoxLayout.insertWidget()`, the widget is wrapped in a
# `QWidgetItem` object that does its own calculation of `maximumSize()`
# rather than defer to the contained widget or its layout.

def __init__( self, widget ) :

QtWidgets.QWidgetItem.__init__( self, widget )

def maximumSize( self ) :

a = self.alignment()
self.setAlignment( QtCore.Qt.Alignment() )
s = QtWidgets.QWidgetItem.maximumSize( self )
self.setAlignment( a )

return s

## The ListContainer holds a series of Widgets either in a column or a row.
# It attempts to provide a list like interface for manipulation of the widgets.
class ListContainer( GafferUI.ContainerWidget ) :
Expand Down Expand Up @@ -86,7 +108,12 @@ def append( self, child, expand=False, horizontalAlignment=None, verticalAlignme
self.__widgets.append( child )

stretch = 1 if expand else 0
self.__qtLayout.addWidget( child._qtWidget(), stretch, self.__convertToQtAlignment( horizontalAlignment, verticalAlignment ) )

if horizontalAlignment is not None or verticalAlignment is not None :
self.__qtLayout.addItem( self.__widgetItem( child._qtWidget(), horizontalAlignment, verticalAlignment ) )
else :
self.__qtLayout.addWidget( child._qtWidget(), stretch, self.__convertToQtAlignment( horizontalAlignment, verticalAlignment ) )

child._applyVisibility()

def remove( self, child ) :
Expand All @@ -106,7 +133,11 @@ def insert( self, index, child, expand=False, horizontalAlignment=None, vertical
self.__widgets.insert( index, child )

stretch = 1 if expand else 0
self.__qtLayout.insertWidget( index, child._qtWidget(), stretch, self.__convertToQtAlignment( horizontalAlignment, verticalAlignment ) )

if horizontalAlignment is not None or verticalAlignment is not None :
self.__qtLayout.insertItem( index, self.__widgetItem( child._qtWidget(), horizontalAlignment, verticalAlignment ) )
else :
self.__qtLayout.insertWidget( index, child._qtWidget(), stretch, self.__convertToQtAlignment( horizontalAlignment, verticalAlignment ) )
child._applyVisibility()

def index( self, child ) :
Expand Down Expand Up @@ -178,6 +209,16 @@ def __len__( self ) :

return len( self.__widgets )

def __widgetItem( self, qtWidget, horizontalAlignment, verticalAlignment ) :

# Duplicate the logic in `QBoxLayout.insertWidget()` but with our
# customized `QWidgetItem`.
self._qtWidget().layout().addChildWidget( qtWidget )
widgetItem = _WidgetItem( qtWidget )
widgetItem.setAlignment( self.__convertToQtAlignment( horizontalAlignment, verticalAlignment ) )

return widgetItem

def __convertToQtAlignment( self, horizontalAlignment, verticalAlignment):

if not horizontalAlignment and not verticalAlignment:
Expand Down
14 changes: 14 additions & 0 deletions python/GafferUITest/ListContainerTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,5 +370,19 @@ def testSliceSetItemAtEnd( self ) :
self.assertEqual( c[0].s, "a" )
self.assertEqual( c[1].s, "b" )

def testAlignmentSize( self ) :

with GafferUI.Window( "Test" ) as w :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Vertical ) as v :
l1 = GafferUI.Label( "test", parenting = { "verticalAlignment" : GafferUI.VerticalAlignment.Top } )
l2 = GafferUI.Label( "test" )

w._qtWidget().resize( 200, 200 )
w.setVisible( True )
self.waitForIdle()

self.assertEqual( v.size().y, l1.size().y + l2.size().y )
self.assertEqual( l2._qtWidget().pos().y(), l1.size().y )

if __name__ == "__main__":
unittest.main()
Loading