2929import maya .common .ui as mui
3030
3131from mayaUsdLibRegisterStrings import getMayaUsdLibString
32+ from .dragAndDropTextField import DragAndDropTextField
3233
3334@dataclass (slots = True )
3435class MaterialPurposeUI :
@@ -51,7 +52,7 @@ class MaterialCustomControl(object):
5152 getMayaUsdLibString ('kLabelStrongerMaterial' ) : 'strongerThanDescendants' ,
5253 }
5354
54- TextField = collections .namedtuple ('TextField' , ['layout' , 'field' , 'button ' , 'graphMenu' ])
55+ TextField = collections .namedtuple ('TextField' , ['layout' , 'field' , 'gotoButton ' , 'graphButton' , 'graphMenu' ])
5556
5657 @staticmethod
5758 def hasMaterial (prim ):
@@ -100,6 +101,7 @@ def onCreate(self, *args):
100101 for purpose in [UsdShade .Tokens .allPurpose , UsdShade .Tokens .preview , UsdShade .Tokens .full ]:
101102 textField = self .materialPurposeUIs [purpose ].material .field
102103 self ._connectTextFieldChangeCallback (purpose , textField )
104+ self ._connectTextFieldImmediateChangeCallback (purpose , textField )
103105
104106 # Fill the UI.
105107 self .refresh ()
@@ -131,84 +133,94 @@ def _createMaterialUI(self, purpose):
131133 '''
132134 purposeName = self ._getPurposeForLabels (purpose )
133135
134- # Note: icon image taken from LookdevX plugin.
135- hasLookdevX = self ._hasLookdevX ()
136- graphIcon = 'LookdevX.png' if hasLookdevX else None
137-
138136 purposeUI = self .materialPurposeUIs [purpose ]
139- purposeUI .material = self ._createTextField (
140- 'material' ,
141- f'kLabel{ purposeName } Material' ,
142- f'kAnn{ purposeName } Material' ,
143- graphIcon ,
144- 'kAnnShowMaterialInLookdevx' , True )
137+ purposeUI .material = self ._createTextField ('material' , f'kLabel{ purposeName } Material' , f'kAnn{ purposeName } Material' , canGraph = True )
145138
146139 def _createInheritedUI (self , purpose ):
147140 '''
148141 Create the UI for a given material purpose.
149142 '''
150143 purposeName = self ._getPurposeForLabels (purpose )
151144
152- # Note: icon image taken from LookdevX plugin.
153- hasLookdevX = self ._hasLookdevX ()
154- graphIcon = 'LookdevX.png' if hasLookdevX else None
155-
156145 purposeUI = self .materialPurposeUIs [purpose ]
157- purposeUI .inherited = self ._createTextField ('inherited' , f'kLabel{ purposeName } InheritedMaterial' , image = graphIcon , imageTooltipRes = 'kAnnShowMaterialInLookdevx' , canGraph = True )
146+ purposeUI .inherited = self ._createTextField ('inherited' , f'kLabel{ purposeName } InheritedMaterial' , canGraph = True )
158147 # Note: inArrow.png icon image taken from Maya resources.
159- purposeUI .fromPrim = self ._createTextField ('from prim' , f'kLabel{ purposeName } InheritedFromPrim' , image = 'inArrow.png' )
148+ purposeUI .fromPrim = self ._createTextField ('from prim' , f'kLabel{ purposeName } InheritedFromPrim' )
160149
161- def _createTextField (self , longName , uiNameRes , uiTooltipRes = None , image = None , imageTooltipRes = None , canGraph = False ):
150+ def _createTextField (self , longName , uiLabelRes , uiTooltipRes = None , canGraph = False ):
162151 '''
163152 Create a disabled text field group and an optional image button with the correct label.
164153 '''
165- uiLabel = getMayaUsdLibString (uiNameRes ) if self .useNiceName else longName
154+ uiLabel = getMayaUsdLibString (uiLabelRes ) if self .useNiceName else longName
166155 uiTooltip = getMayaUsdLibString (uiTooltipRes ) if uiTooltipRes else uiLabel
167- rowLayout = cmds .rowLayout (numberOfColumns = 3 , adjustableColumn3 = 2 )
156+ rowLayout = cmds .rowLayout (numberOfColumns = 4 , adjustableColumn4 = 2 )
168157 with mui .LayoutManager (rowLayout ):
169158 cmds .text (label = uiLabel , annotation = uiTooltip )
170- textField = cmds .textField (annotation = uiTooltip , editable = False , enableKeyboardFocus = True )
171- if image :
172- imageTooltip = getMayaUsdLibString (imageTooltipRes ) if imageTooltipRes else ''
173- button = cmds .symbolButton (enable = False , image = image , annotation = imageTooltip )
159+ textField = DragAndDropTextField (uiTooltip )
160+ gotoButton = cmds .symbolButton (enable = False , image = 'inArrow.png' )
161+
162+ hasLookdevX = self ._hasLookdevX ()
163+ if canGraph and hasLookdevX :
164+ # Note: icon image taken from LookdevX plugin.
165+ graphIcon = 'LookdevX.png' if hasLookdevX else None
166+
167+ graphTooltip = getMayaUsdLibString ('kAnnShowMaterialInLookdevx' )
168+ graphButton = cmds .symbolButton (enable = False , image = graphIcon , annotation = graphTooltip )
169+ graphMenu = self ._createGraphMenu (graphButton )
174170 else :
175- button = None
171+ graphButton = None
172+ graphMenu = None
176173
177- if canGraph :
178- graphMenu = self ._createGraphMenu (button )
179- else :
180- graphMenu = None
174+ return MaterialCustomControl .TextField (rowLayout , textField , gotoButton , graphButton , graphMenu )
181175
182- return MaterialCustomControl .TextField (rowLayout , textField , button , graphMenu )
176+ def _setMaterialPurposeBinding (self , purpose , value ):
177+ try :
178+ ufePath = ufe .PathString .string (self .item .path ())
179+ if value :
180+ cmd = mayaUsd .ufe .BindMaterialCommand (ufePath , value , purpose )
181+ else :
182+ cmd = mayaUsd .ufe .UnbindMaterialCommand (ufePath , purpose )
183+ ufeCmdWrapper .execute (cmd )
184+ except Exception as e :
185+ cmds .warning (f'Error executing material command: { e } ' , noContext = True )
186+ self .refresh ()
183187
184188 def _connectTextFieldChangeCallback (self , purpose , textField ):
189+ def callback (value ):
190+ self ._setMaterialPurposeBinding (purpose , value )
185191
186- @mayaUsdUtils .setUndoLabel (getMayaUsdLibString ('kLabelSetMaterialBindingUndo' ))
187- def callback (value , * args , ** kwargs ):
192+ textField .setChangeCallback (callback , getMayaUsdLibString ('kLabelSetMaterialBindingUndo' ))
193+
194+ def _connectTextFieldImmediateChangeCallback (self , purpose , textField ):
195+ def callback (newValue , wasDropped ):
196+ if not wasDropped :
197+ return
198+ self ._setMaterialPurposeBinding (purpose , newValue )
199+
200+ def validation (lastValue , newValues ):
188201 try :
189- ufePath = ufe .PathString .string (self .item .path ())
190- if value :
191- cmd = mayaUsd .ufe .BindMaterialCommand (ufePath , value , purpose )
192- else :
193- cmd = mayaUsd .ufe .UnbindMaterialCommand (ufePath , purpose )
194- ufeCmdWrapper .execute (cmd )
202+ for newValue in newValues :
203+ if not newValue :
204+ continue
205+ if newValue [0 ] not in ['|' , '/' ]:
206+ continue
207+ newValue = newValue .split (',' )[- 1 ]
208+ # TODO: validate that it is a material?
209+ return newValue
195210 except Exception as e :
196- print (f'Error executing material command : { e } ' )
197- self . refresh ()
211+ cmds . warning (f'Error validating material path : { e } ' , noContext = True )
212+ return None
198213
199- try :
200- cmds .textField (textField , edit = True , changeCommand = callback )
201- except Exception as e :
202- print (f'Error connecting text field change callback: { e } ' )
214+ textField .setImmediateChangeCallback (validation , callback , getMayaUsdLibString ('kLabelSetMaterialBindingUndo' ))
203215
204- def _createGraphMenu (self , button ):
216+ def _createGraphMenu (self , graphButton ):
205217 '''
206218 Create a popup menu attached to the given button to graph a material.
207219 '''
208- if not button :
220+ if not graphButton :
209221 return None
210222
211- return cmds .popupMenu (parent = button , button = True )
223+ return cmds .popupMenu (parent = graphButton , button = True )
212224
213225 def _createDropDownField (self , longName , uiNameRes , elementsRes ):
214226 '''
@@ -229,7 +241,7 @@ def callback(value, *args, **kwargs):
229241 # Force update of all children in the VP2 delegate.
230242 cmds .evalDeferred (lambda : ufe .Scene .notify (ufe .ObjectRename (self .item , self .item .path ())))
231243 except Exception as e :
232- print (f'Error executing material command: { e } ' )
244+ cmds . warning (f'Error executing material command: { e . args [ 0 ] if e . args else e } ' , noContext = True )
233245 self .refresh ()
234246
235247 uiLabel = getMayaUsdLibString (uiNameRes ) if self .useNiceName else longName
@@ -328,27 +340,30 @@ def _fillUIForPurpose(self, purpose, mat, matRel, directMat):
328340 cmds .rowLayout (purposeUI .inherited .layout , edit = True , visible = False )
329341 cmds .rowLayout (purposeUI .fromPrim .layout , edit = True , visible = False )
330342
331- self ._fillGraphButton (text , purposeUI .material .button , purposeUI .material .graphMenu )
332- self ._fillGraphButton (inherited , purposeUI .inherited .button , purposeUI .inherited .graphMenu )
333- self ._fillGotoPrimButton (purposeUI , fromPathStr )
343+ self ._fillGraphButton (text , purposeUI .material .graphButton , purposeUI .material .graphMenu )
344+ self ._fillGraphButton (inherited , purposeUI .inherited .graphButton , purposeUI .inherited .graphMenu )
345+
346+ self ._fillGotoPrimButton (purposeUI .material .gotoButton , text )
347+ self ._fillGotoPrimButton (purposeUI .inherited .gotoButton , inherited )
348+ self ._fillGotoPrimButton (purposeUI .fromPrim .gotoButton , fromPathStr )
334349
335- cmds . textField ( purposeUI .material .field , edit = True , editable = True , text = text , placeholderText = placeholder , annotation = annotation )
336- cmds . textField ( purposeUI .inherited .field , edit = True , text = inherited )
337- cmds . textField ( purposeUI .fromPrim .field , edit = True , text = fromPathStr )
350+ purposeUI .material .field . fillUI ( text , placeholder , annotation )
351+ purposeUI .inherited .field . fillUI ( inherited , editable = False )
352+ purposeUI .fromPrim .field . fillUI ( fromPathStr , editable = False )
338353
339- def _fillGraphButton (self , matPathStr , button , menu ):
354+ def _fillGraphButton (self , matPathStr , graphButton , menu ):
340355 '''
341356 Fill the graph button with the correct command.
342357 '''
343358 # Note: only show the graph button if LookdevX was loaded when the UI
344359 # was created.
345- if not button :
360+ if not graphButton :
346361 return
347362
348363 # Note: only show the graph button if LookdevX is currently loaded.
349364 hasLookdevX = self ._hasLookdevX ()
350365 canGraph = bool (matPathStr and hasLookdevX )
351- cmds .symbolButton (button , edit = True , enable = canGraph , visible = hasLookdevX )
366+ cmds .symbolButton (graphButton , edit = True , enable = canGraph , visible = hasLookdevX )
352367
353368 if canGraph :
354369 ufePathStr = self ._createUFEPathFromUSDPath (matPathStr )
@@ -414,20 +429,20 @@ def _showInExistingTab(ignore, tabName, ufePathStr):
414429 return
415430 cmds .lookdevXGraph (tabName = tabName , graphObject = ufePathStr )
416431
417- def _fillGotoPrimButton (self , purposeUI , fromPath ):
432+ def _fillGotoPrimButton (self , gotoButton , gotoUsdPath ):
418433 '''
419434 Fill the goto-prim button with the correct command.
420435 '''
421- showButton = bool (fromPath )
422- cmds .symbolButton (purposeUI . fromPrim . button , edit = True , enable = showButton , visible = showButton )
436+ showButton = bool (gotoUsdPath )
437+ cmds .symbolButton (gotoButton , edit = True , enable = showButton , visible = showButton )
423438
424- if fromPath :
425- ufePathStr = self ._createUFEPathFromUSDPath (fromPath )
439+ if gotoUsdPath :
440+ ufePathStr = self ._createUFEPathFromUSDPath (gotoUsdPath )
426441 melCommand = 'updateAE "%s"' % ufePathStr
427442 command = lambda * _ : mel .eval (melCommand )
428443 else :
429444 command = ''
430- cmds .symbolButton (purposeUI . fromPrim . button , edit = True , command = command )
445+ cmds .symbolButton (gotoButton , edit = True , command = command )
431446
432447 def _createUFEPathFromUSDPath (self , usdPath ):
433448 '''
0 commit comments