diff --git a/UGV4.tox b/UGV4.tox index e4d7651..40c50fd 100644 Binary files a/UGV4.tox and b/UGV4.tox differ diff --git a/UberGui_V4_Release.toe b/UberGui_V4_Release.toe index 6023756..69e2e29 100644 Binary files a/UberGui_V4_Release.toe and b/UberGui_V4_Release.toe differ diff --git a/css/CSS.css b/css/CSS.css index ae3316c..aeb844d 100644 --- a/css/CSS.css +++ b/css/CSS.css @@ -101,7 +101,7 @@ html, body { line-height: |+|Parameterrowheight|+|px; margin-right: |+|Middlemargins|+|px; height: 100%; - width: 50%; + width: |+|Labelvaluesplit|+|%; background-color: rgb(|+|Labelbgcolor|+|); box-sizing: border-box; transition: var(--hover-transition-time); @@ -144,7 +144,7 @@ background-color: rgb(|+|Primaryinteractcolor|+|); margin: 0pt; padding: 0pt; height: 100%; - width: 50%; + width: |+|Labelvaluesplitinverse|+|%; display: flex; } @@ -167,7 +167,7 @@ background-color: rgb(|+|Primaryinteractcolor|+|); line-height: |+|Parameterrowheight|+|px; padding: 0pt; height: 100%; - flex: 0 0 20px; + flex: 0 0 |+|Parameterrowheight|+|px; background-color: rgb(|+|Parameterbgcolor|+|); box-sizing: border-box; border-width:0pt; diff --git a/javascript/JS.js b/javascript/JS.js index 1cdf572..c487b0c 100644 --- a/javascript/JS.js +++ b/javascript/JS.js @@ -18,13 +18,16 @@ function Update_ ( jsonStr ) { { val = Math.round( parseFloat(val)*100 ) / 100; } - + var textEl = document.getElementById( id + '_t' ); textEl.innerHTML = val; var width = textEl.offsetWidth; var slideEl = document.getElementById( id + '_s' ); - slideEl.style.width = (slide*100)+'%'; + + // document.getElementById( "debug" ).innerHTML = clamp(slide*100 , 0.0 , 100.0); + + slideEl.style.width = clamp(slide*100 , 0.0 , 100.0)+'%'; }; @@ -120,6 +123,8 @@ function Mouse_( jsonStr ) { var x = jsonObj['x']; var y = jsonObj['y']; var argPar = jsonObj['par']; + + // document.getElementById( "debug" ).innerHTML = argPar; var isScrollHover = 0; if (x > document.body.clientWidth){ @@ -180,7 +185,7 @@ function Mouse_( jsonStr ) { } } else if ( Last_Par_Id == Current_Par_Id ) { - document.getElementById( "debug" ).innerHTML = deepestElement.parentNode.parentNode.nextElementSibling.className; + // document.getElementById( "debug" ).innerHTML = deepestElement.parentNode.parentNode.nextElementSibling.className; if ( deepestElement.parentNode.parentNode.nextElementSibling.className == "spacer_section" ){ Next_Par_Id = deepestElement.parentNode.parentNode.nextElementSibling.nextElementSibling.children[2].firstElementChild.id; } diff --git a/python/UG4.py b/python/UG4.py index 7a216cb..c43607a 100644 --- a/python/UG4.py +++ b/python/UG4.py @@ -4,6 +4,15 @@ class UG4: def __init__(self, ownerComp): self.ownerComp = ownerComp + self.paramInfo = op('null_paramInfo') + self.webInfo = op('WEB_INFO') + self.webRenderTop = op('WEB_RENDER') + self.scrollTimer = op('timer_scrollChange') + self.srcOpDat = op('null_srcOp') + self.dstOpsDat = op('null_dstOps') + self.tupleLookup = op('null_tupleLookup') + + self.uvLselect = op('null_uv_lselect') def Regenerate(self, SRC ): @@ -224,9 +233,127 @@ def ParseTitle(self,titleStr): except: return None - + def Update_Changed_Params( self , rows ): + ''' + This function monitors lots of deeper parameter data from the source object, and when any of it changes, + it attempts to re-calculate the displayed value, and slider position if applicable. The parameter dat really + makes this process awesome, because it outputs min/max settings, section, menu names, etc which can alter + the range of a slider for instance. + ''' + + # define some things. + SRC = op(self.srcOpDat[0,0]) + uberGuiOverrideDat = SRC.op('Uberguiconfigoverride') + data = [] + + # iterate over all the table rows that changed.. + for rowID in rows: + + # collect some preliminary data bout the changed row/parameter. + style = self.paramInfo[rowID,'style'].val + name = self.paramInfo[rowID,'name'].val + tupletname = self.paramInfo[rowID,'tupletname'].val + + # just placeholders, we'll fill these in down below. + value = '' + slide = 0 + + # Edge Case #1 : menu params. We want to display + # the selected item's menu label. so some lookup stuff happens for that. + # then we calc the slider position based on the index of the chosen item. + if style in [ 'Menu' , 'StrMenu' ]: + menuindex = int(self.paramInfo[rowID,'menuindex']) + menulabels = self.paramInfo[rowID,'menulabels'].val + menulabels = eval(menulabels) + try: # using try/except so that we can silently fail for StrMenu types. + value = menulabels[menuindex] + slide = tdu.remap( menuindex , 0 , len(menulabels)-1 , 0 , 1 ) + except: # if user puts in incorrect string to StrMenu, we just set to 0/blank. + value = '' + slide = 0 + + # Edge Case #2 : buttons that can have state changes. + # conveniently, the param DAT outputs menu items ['off', 'on'] even for buttons, so + # we can take advantage of that for a value to show, and set our slider to 0% or 100% + elif style in [ 'Momentary' , 'Toggle' ]: + + menuindex = int(self.paramInfo[rowID,'value']) + menulabels = self.paramInfo[rowID,'menulabels'].val + menulabels = eval(menulabels) + value = menulabels[menuindex] + slide = menuindex + + + # Edge Case #3 : pulse buttons don't really have states, they just trigger things instantly. + # So for these we want the slider to always be 0%, and the value to just be the param label. + # this encourages developers to name the pulse button's label something action oriented, + # for instance "Open Window" or "Start Render", etc. + elif style in [ 'Pulse' ]: + + value = self.paramInfo[rowID,'label'].val + slide = 0 + + # Ok done with edge cases, we are able to process all other parameter types under this next logical branch! + else: + + # get the custom user provided format of the parameter, if there is any. + if uberGuiOverrideDat != None: + CustomFormat = uberGuiOverrideDat[tupletname,'style'].val if uberGuiOverrideDat[tupletname,'style'] != None else None + CustomFormat = CustomFormat if CustomFormat != '' else None + else: + CustomFormat = None + + # If no custom format was specified, do the default thing. + if CustomFormat == None: + + # try to treat it numerically, but if it fails, it's probably string based. + try: + value = float(self.paramInfo[rowID,'value']) + value = int(float(value) * 1000) / 1000 + normmin = float(self.paramInfo[rowID,'normmin']) + normmax = float(self.paramInfo[rowID,'normmax']) + slide = tdu.remap( value , normmin , normmax , 0 , 1 ) + + # assuming it's a string. + except: + value = self.paramInfo[rowID,'value'].val + slide = 0 + + # one of the custom ubergui override formats is Rgba255. + # This essentially allows you to work with color values as integers, in the range of 0-255. + elif CustomFormat in [ 'Rgba255' ]: + + # try to treat it numerically, but if it fails, it's probably string based. + try: + value = float(self.paramInfo[rowID,'value']) + normmin = float(self.paramInfo[rowID,'normmin']) + normmax = float(self.paramInfo[rowID,'normmax']) + slide = tdu.remap( value , normmin , normmax , 0 , 1 ) + value = int(round(value * 255)) + + # assuming it's a string. + except: + value = self.paramInfo[rowID,'value'].val + slide = 0 + + # if CustomFormat is spaceToUnderscore or tduLegal this means we are working with a name parameter + # or something like that, where we cannot have spaces. Since this script is dealing with display only values + # it's assumed the string is already formatted correctly and this is just placeholder incase we need to + # do more with the display side of this edge case. + elif CustomFormat in [ 'spaceToUnderscore' , 'tduLegal' ]: + value = self.paramInfo[rowID,'value'].val + slide = 0 + + # add the changed names, value, and slider pos to our data list + data += [[ name , value , slide ]] + + # Update those changes to the web render top. + self.Update( op('WEB_RENDER') , data ) + + return + def Update(self , webRenderTop , flatArgList ): - + jsonArgsList = json.dumps(flatArgList).replace("'", '"') script = "Update_('{0}')".format(jsonArgsList) @@ -244,6 +371,715 @@ def Mouse(self , webRenderTop , x=0, y=0 , targetPar='' ): script = "Mouse_('{0}')".format(jsonArgsList) webRenderTop.executeJavaScript(script) webRenderTop.interactMouse( x , parent.Widget.height-y , pixels=True ) + + + def Interact_LeftClick_Down( self ): + ''' + Interact_LeftClick_Down is the "DownClick", where we calculate a bunch of things, and do the hard work so + the _While function can operate as quickly as possibly every frame down below. + we use node storage to store these initial values. + ''' + + # Get the html title, which contains the 'WebRenderPick' custom data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + self.ownerComp.store("pikDict" , pikDict) + + # If the user left clicked somewhere, we can be sure the auxilary UI stuffs should be closed, if not already. + op('field').Close() + op('menu').Close() + op('colorpicker').Close() + op('tooltip').Close() + + # first lets make sure our pikDict exists... + if pikDict != None: + + # If it does, extract our mouse position, bottom pixel coord, and hovered element/Par name. + initX = float( pikDict["X"] ) + initY = float( pikDict["Y"] ) + bottom = float( pikDict["bottom"] ) + initParName = str( pikDict["Par"] ) + # initParValue = self.paramInfo[initParName,'value'].val if self.paramInfo[initParName,'menuindex'].val == '' else self.paramInfo[initParName,'menuindex'].val + + # we only have 1 source object feeding our UI generation, but we could be addressing + # the parameters of multiple objects ! get those object(s) now and store them. + dstOps = [ x[0].val for x in self.dstOpsDat.rows() ] + self.ownerComp.store("initOps" , dstOps) + + # get and store a list of the values of all the parameters in our source object. + ValueState = list(map(str,self.paramInfo.col('value'))) + self.ownerComp.store('ValueState' , ValueState) + + # now that our data is prepared, lets handle our numerous edge cases. The user might + # have their mouse over a label, or maybe a spacer. They might also have it over a slider, etc. + # Edge Case #1 : Labels. + if initParName.endswith('_l'): # is a label. + + # take off the suffix, now that we are in label branch. + initParName = initParName.replace('_l', '') + + # get the first matching parameter name, of the tuple. + # A parameter even if a single value param, is always part of a Tuple, so we can assume length of >= 1 + initParName = [ x.offset(0,1).val for x in self.tupleLookup.findCells(initParName , cols=[0]) ][0] + + # list comp convert our destination operators to a list of destimation parameters, then store it. + foundPars = [ getattr( op(x).par , initParName , ":PAR_ERR:" ) for x in dstOps ] + self.ownerComp.store("initPar" , foundPars) + + # determine the style of the parameter, and store it. + style = self.paramInfo[initParName,'style'] + self.ownerComp.store("style" , style) + + # Edge Case #2 : Spacers - not much to do here. + elif initParName == '_spacer_': # is a spacer + + self.ownerComp.store("parCick" , 0) + self.ownerComp.store("style" , '') + + # Edge Case #3 : Tool Tip. + elif initParName.endswith('_tt'): # is a tooltip. + + # take off the suffix, now that we are in this branch. + toolTipName = initParName.replace('_tt', '') + + # get the first destination op, as the src for the tooltop. + srcOp = op(dstOps[0]) + + # if object exists, proceed. + if srcOp != None: + + # get the ubergui config override dat. our tooltip should be in here. + configFile = srcOp.op('Uberguiconfigoverride') + + # if the config file exists, proceed. NOTE: it doesn't have to exist. + if configFile != None: + + # if it does exist, get the matching cell, which is the tooltip text. + toolTip = configFile[ toolTipName , 1 ] + + # if the cell exists, we can launch the tooltip! + # NOTE: it doesn't have to exist. + if toolTip != None: + op('tooltip').Launch( BOTTOM=bottom , MSG=toolTip.val ) + + # Edge Case #4 : File or Folder Chooser. + elif initParName.endswith('_fp'): # file or folder chooser. + + # take off the suffix, now that we are in this branch., get the param style. + initParName = initParName.replace('_fp','') + style = self.paramInfo[initParName,'style'] + + # sub branch: + if style == "File": # if file, launch file chooser. + path = ui.chooseFile(title="choose a file.") + + elif style == "Folder": # if folder, launch folder chooser. + path = ui.chooseFolder(title="choose a folder.") + + # for each of the destination objects, set the new path to the matching parameter + for each in self.dstOpsDat.rows(): + t = op(each[0]) + if t != None: + foundPar = getattr( t.par , initParName , ":PAR_ERR:" ) + else: + foundPar = ":PAR_ERR:" + if foundPar != ":PAR_ERR:": + foundPar.val = path + + # Edge Case #5 : Menu Picker. + elif initParName.endswith('_mp'): # menu picker + + # take off the suffix, now that we are in this branch. + initParName = initParName.replace('_mp','') + + # get some dimensions of our UI, so we can intelligently overlay our menu picker! + left = self.ownerComp.width/2 + right = float( pikDict["right"] ) + top = float( pikDict["top"] ) + bottom = float( pikDict["bottom"] ) + style = str( self.paramInfo[initParName,'style'] ) + + # launch menu picker. + op('menu').Launch( dstOps , initParName , left , right , bottom , top ) + + # Edge Case #6 : Color Picker. + elif initParName.endswith('_cp'): # color picker + + # take off the suffix, now that we are in this branch. + initParName = initParName.replace('_cp','') + + # get some dimensions of our UI, so we can intelligently overlay our color picker! + left = self.ownerComp.width/2 + right = float( pikDict["right"] ) + top = float( pikDict["top"] ) + bottom = float( pikDict["bottom"] ) + style = str( self.paramInfo[initParName,'style'] ) + + # launch color picker. + op('colorpicker').Launch( dstOps , initParName , left , right , bottom , top ) + + # Edge Case #7 : Everything else IE. widget parameter sliders! + else: # is a widget. + + # store an indicator that we clicked an actual par, for later. + me.store("parCick" , 1) + + # if init par existed, fetch style, and normmin and normmax from the paramInfo table. + if self.paramInfo[initParName,'style'] != None: + style = str( self.paramInfo[initParName,'style'] ) + normMin = float( self.paramInfo[initParName,'normmin'] ) + normMax = float( self.paramInfo[initParName,'normmax'] ) + + # Otherwise, just nullify the variables. + else: + style = '' + normMin = 0 + normMax = 0 + + # try to get menu item list as a python list, if it fails, it's because it's not a menu param. + try: + menunames = eval( str(self.paramInfo[initParName,'menunames']) ) + except: + menunames = [] + + # using list comp, filter our destination operators down to ones that are real. + foundDstOps = [ op(x) for x in dstOps if op(x) != None ] + + # list comp convert our dest ops to list of actual parameters. + foundPars = [ getattr( dstOp.par , initParName , ":PAR_ERR:" ) for dstOp in foundDstOps ] + + # filter out pars that did not exist on some objects. + foundPars = [ each for each in foundPars if each != ":PAR_ERR:" ] + + # store some initial x/y position values, and our parameter list. + # storing these paramters now means we don't have to re-look them up every frame during a drag. more efficient! + self.ownerComp.store("initX" , initX) + self.ownerComp.store("initY" , initY) + self.ownerComp.store("initPar" , foundPars) + + # we also need the initial value of the parameter, so we know how much relative, we've adjusted the value. + self.ownerComp.store('initVal' , [each.eval() for each in foundPars]) + + # we also need some other attributes of our parameter during our drag operations, store those too. + self.ownerComp.store("normMin" , normMin) + self.ownerComp.store("normMax" , normMax) + self.ownerComp.store("style" , style) + self.ownerComp.store("menunames" , menunames) + self.ownerComp.store("initParNAME" , initParName) + # self.ownerComp.store("initParVAL" , initParValue) + # print(initParName) + + # if the parameter style is one that can have a slider graphic, we need to set our DragOverlay to mask our active parameter. + if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'XY' , 'XYZ' , 'WH' , 'Int' , 'Menu' , 'StrMenu' ]: + self.Set_DragOverlay( self.webRenderTop , 1 , initParName ) + + # if the parameter style is one that is a button like par, that can have a state, we also want to enable the DragOverlay mask, + # but really just so we're not dragging/affecting other params accidentally during a rushed drag/click. + elif style in [ 'Momentary' , 'Toggle' ]: + self.Set_DragOverlay( self.webRenderTop , 1 , initParName ) + for thisPar in foundPars: + if thisPar != ":PAR_ERR:": + + # just invert the buttons state on this down click. + thisPar.val = 1 - thisPar.eval() + + # if the parameter style is a pulse param, there is no state, but we still want to enable the DragOverlay mask. + # so we're not dragging/affecting other params accidentally during a rushed drag/click. + elif style in [ 'Pulse' ]: + + self.Set_DragOverlay( self.webRenderTop , 1 , initParName ) + for thisPar in foundPars: + if thisPar != ":PAR_ERR:": + + + # just pulse the parameter, it's the only thing we can do with these types. + thisPar.pulse() + + return + + + def Interact_LeftClick_While( self , uDist , vDist , inputType ): + ''' + Now that we have done the hard work and stored some initial values, we can deal + with our user's dragging actions performed while the left mouse button is pressed down. + ''' + + # Get the html title, which contains the 'WebRenderPick' custom data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # ensure we're dealing with valid webRenderPick data! + if pikDict != None: + + # get the initial par name, and the dragX position from the renderpick. + initParName = str( pikDict["Par"] ) + + if inputType == 'mouse': + dragX = float( pikDict["X"] ) + elif inputType == 'touch': + leftEdge = float( pikDict["left"] ) + rightEdge = float( pikDict["right"] ) + dragX = float( uDist ) + dragX = dragX * self.ownerComp.width + dragX = tdu.clamp( dragX , leftEdge , rightEdge ) + dragX = tdu.remap( dragX, leftEdge , rightEdge, 0 , 1 ) + + # determine if user has dragged mouse any at all. + # a careful unmoving click is different logic than a moving drag. + uDistAbs = abs(uDist) + vDistAbs = abs(vDist) + hasUserDraggedMouse = max(uDistAbs,vDistAbs) != 0 + + # if user is dragging over a label, that is fine. we just want the prefix. + if initParName.endswith('_l'): + initParName = initParName.replace('_l', '') + + # if user is dragging over a tooltip, that is fine. we just want the prefix. + elif initParName.endswith('_tt'): + initParName = initParName.replace('_tt', '') + + # else, assume user is dragging over the parameter slider. + else: + pars = self.ownerComp.fetch("initPar" , []) + + # only proceed if we have at least 1 par. Should only have one anyways. + if len(pars): + + # get the first par. + par = pars[0] + + # make sure our pick data and par are valid. + if pikStr != "" and par != ":PAR_ERR:": + + # fetch a bunch of our initial data stored during down click. + initX = self.ownerComp.fetch("initX" , 0) + initY = self.ownerComp.fetch("initY" , 0) + normMin = self.ownerComp.fetch("normMin" , 0) + normMax = self.ownerComp.fetch("normMax" , 1) + style = self.ownerComp.fetch("style" , '') + menunames = self.ownerComp.fetch("menunames" , []) + + # only proceed with this branch IF user has dragged mouse away from initial position. + if hasUserDraggedMouse: + # Edge Case #1 : any standard float value slider. We just remap from 0-1 to normMin-normMax + if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'XY' , 'XYZ' , 'WH' ]: + + newVal = tdu.remap( dragX , 0 , 1 , normMin , normMax )# + float(initParValue) + + # set this value to all the pars of the selected objects. + for each in pars: + if each != ":PAR_ERR:": + each.val = newVal + + # Edge Case #2 : any standard int value slider. We just remap from 0-1 to normMin-normMax, but then round it too. + elif style in [ 'Int' ]: + newVal = tdu.remap( dragX , 0 , 1 , normMin , normMax )# + int(initParValue) + newVal = round(newVal) + + # set this value to all the pars of the selected objects. + for each in pars: + if each != ":PAR_ERR:": + each.val = newVal + + # Edge Case #3 : any fixed Menu param. We still remap from 0-1 to normMin-normMax, and round. + elif style in [ 'Menu' ]: + newVal = tdu.remap( dragX , 0 , 1 , 0 , len(menunames)-1 )# + int(initParValue) + newVal = int( round(newVal) ) + + # thanks to menuIndex member, we can set menu's like an integer parameter. + for each in pars: + if each != ":PAR_ERR:": + each.menuIndex = newVal + + # Edge Case #4 : any StrMenu param. We will set this the same as above, but leave this branch here + # incase we want to add specifics to the StrMenu logic later. + elif style in [ 'StrMenu' ]: + + newVal = tdu.remap( dragX , 0 , 1 , 0 , len(menunames)-1 )# + int(initParValue) + newVal = int( round(newVal) ) + + # thanks to menuIndex member, we can set menu's like an integer parameter. + for each in pars: + if each != ":PAR_ERR:": + each.menuIndex = newVal + return + + + def Interact_LeftClick_Up( self ): + ''' + # Most of the work is done, but the up click still has some logic stuffs that needs to happen. + ''' + # Get the html title, which contains the 'WebRenderPick' custom data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # fetch some of the things we stored on the down click. + pars = self.ownerComp.fetch("initPar" , []) + initVals = self.ownerComp.fetch("initVal" , []) + dstOps = self.ownerComp.fetch("initOps" , []) + ValueStateDown = self.ownerComp.fetch('ValueState',[]) + + # get the current state of the parameter. + ValueStateUp = list(map(str,self.paramInfo.col('value'))) + + # we want to call the Parameter Changed script, + # but should check to make sure the value has actually changed. + if (ValueStateUp != ValueStateDown): + self.ownerComp.ParamChange( pars=pars, prevVals=initVals ) + + # if we had a par.. + if len(pars) > 0: + par = pars[0] + + # if our picking info was valid. + if pikDict != None: + + # get the initial param name. + initParName = str( pikDict["Par"] ) + + # If the par name is not the Initial value we get after force restart ... + if initParName != "INIT": + + # get the current X/Y and fetch style from storage. + initX = float( pikDict["X"] ) + initY = float( pikDict["Y"] ) + style = self.ownerComp.fetch("style" , '') + + # we also want the NEXT par in line, as well as it's bounds incase user tabs. + initParName2 = str( pikDict["Par2"] ) + left = float( pikDict["left2"] ) + right = float( pikDict["right2"] ) + top = float( pikDict["top2"] ) + bottom = float( pikDict["bottom2"] ) + + # if the user is finishing a left click on a label, this means we want to launch the field for that parameter. + if initParName.endswith('_l'): + + # of course, some parameter types don't have field support, lets make sure it's not one of those. + if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , 'Momentary' ]: + + # chop off the suffix, and keep the prefix. + parTupletName = initParName.replace('_l','') + + # find the first parameter in the tuple, that the label matches. That's where we want to launch the field. + matchingCells = [ x for x in self.tupleLookup.findCells(parTupletName , cols=[0]) ] + matchingCells = [ x.offset(0,1).val for x in matchingCells ] + initParName = matchingCells[0] + + # Launch the field. + op('field').Launch( dstOps , initParName , left , right , bottom , top ) + parent.Widget.Mouse( self.webRenderTop , x=0 , y=0 , targetPar=initParName2 ) + + # If user finished a left click on a tooltip... do nothing (for now). place holder for later + elif initParName.endswith('_tt'): + pass + + # If user finished a left click on anything else... + else: + + # if the finished click was a momentary button, be sure to set the value back to 0. + # momentary buttons are unique in this way, easier to make a special catch here than + # to change the logic more generally. + if style in [ 'Momentary' , ]: + # par = me.fetch("initPar" , ":PAR_ERR:") + if par != ":PAR_ERR:": + # print(par) + par.val = 0 + + # if the user finshed the left click for a Pulse, we can now call our param change script. + # we needed to wait a bit to give the Pulse time to trigger whatever it triggered. + elif style in [ 'Pulse' , ]: + self.ParamChange( pars=pars, prevVals=initVals ) + + # if we get this from the gui, we should not act, we're probably setting enable flags. + else: + pass + + # finally, but only if our initial parameter was valid, set our DragOverlay mask back to off. + if par != ":PAR_ERR:": + initParName = par.name + self.Set_DragOverlay( self.webRenderTop , 0 , initParName ) + + return + + + def Interact_RightClick_Down( self ): + + # get list of all current values and store. + ValueState = list(map(str,self.paramInfo.col('value'))) + self.ownerComp.store('ValueState' , ValueState) + + # Get the html title, which contains the 'WebRenderPick' custom data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # init some lists. + InitPars = [] + InitVals = [] + + # if picking data is valid. + if pikDict != None: + + # calc the initial par name. + initParName = str( pikDict["Par"] ) + + # Edge Case #1 : User Right Clicked a label. + if initParName.endswith('_l'): + + # Chop off suffix, and keep prefix + initParName = initParName.replace('_l', '') + + # do a tuplet lookup, and get all params in that tuplet row. + foundCells = self.tupleLookup.findCells(initParName, cols=['tupletname']) + desiredParNames = [x.offset(0,1).val for x in foundCells] + + # now iterate through that tuplet row. + for thisOp in self.dstOpsDat.rows(): + thisOp = op(thisOp[0]) + + # for each param, reset to default. + for parName in desiredParNames: + + foundParam = getattr( thisOp.par , parName , ':PAR_ERR:' ) + if foundParam != ':PAR_ERR:': + InitVals += [ foundParam.eval() ] + foundParam.val = foundParam.default + InitPars += [ foundParam ] + + # store init pars and vals + self.ownerComp.store('InitPars', InitPars) + self.ownerComp.store('InitVals', InitVals) + + return + + + def Interact_RightClick_While( self , uDist , vDist ): + return + + + def Interact_RightClick_Up( self ): + + ValueStateDown = self.ownerComp.fetch('ValueState') + InitPars = self.ownerComp.fetch('InitPars') + InitVals = self.ownerComp.fetch('InitVals') + ValueStateUp = list(map(str,self.paramInfo.col('value'))) + if (ValueStateUp != ValueStateDown): + self.ParamChange(pars=InitPars, prevVals=InitVals) + + return + + + + def Interact_Hover( self , select , u , v ): + ''' + select = state of the left select during a hover. Not used for adjusting sliders, but important for logic. + ''' + + # get u position as clamped 0-1 + u = tdu.clamp( u , 0 , 1 ) + + # find out if mouse is in the right side, or left side. + isInRightSide = int(self.GetScrollContext() == 'adjust') + + # get the initParName from the left select script dat storage. + initParName = self.ownerComp.fetch("initParNAME" , '') + forcedParName = ['',initParName][ min( select , isInRightSide) ] + + # calculate the X and Y position in absolute pixel values. This is what the render top wants. + x = int(u * self.ownerComp.width) + x = tdu.clamp(x , 0 , self.ownerComp.width) + y = int( self.ownerComp.height - int(v * self.ownerComp.height) ) + + # send the mouse position, and last initialized par name to Mouse function. + parent.Widget.Mouse( self.webRenderTop, x , y , forcedParName ) + + + + def Interact_Scroll( self , scrollDisplace , inputType , asPixels=False ): + ''' + scrollDisplace: An instantaneous scroll amount, not a cumulative one. + + This function is a wrapper for all scrolling functionality, which usually does not happen + during other functionality like mouse movement, or drag, etc. further more, if user is using + touch input, we want to separate other interactions from scrolling in a dif way. + ''' + + IS_SCROLL = self.ownerComp.GetScrollContext() == 'scroll' + IS_ADJUST = self.ownerComp.GetScrollContext() == 'adjust' + + # if the user was in scroll context... + if IS_SCROLL == True: + + # calculate some booleans based on mouse position. + IsScrollableContext = int( IS_SCROLL ) + IsInsidePanel = int(self.ownerComp.panel.inside) + + # if user provided scroll input via mouse... + if inputType == 'mouse': + CanWeScroll = min( IsScrollableContext , IsInsidePanel ) + Scrollratemultiplier = self.ownerComp.par.Mousescrollratemultiplier.eval() + ExponentialScrollRateMultiplier = abs((scrollDisplace * scrollDisplace)) * self.ownerComp.par.Mousescrollacelleration.eval() + wheel = scrollDisplace * CanWeScroll * Scrollratemultiplier * ExponentialScrollRateMultiplier + elif inputType == 'touch': + Scrollratemultiplier = self.ownerComp.par.Touchscrollratemultiplier.eval() + wheel = scrollDisplace * IsScrollableContext * Scrollratemultiplier + + + # send interaction to the web render top - u/v doesn't matter here since all we want is a scroll comamnd. + self.webRenderTop.interactMouse(0, 0, wheel=wheel , pixels=asPixels) + + # if user scrolled over a value + # funny thing about the mouse wheel, is if you scroll a notch, you get a non-zero value, then a zero. + # zero can obviously be calculated, but it's wasted cpu cycles since it doesn't do anything. + # this if statement only executes a scroll IF the value is one of those non-zero ones. + if IS_ADJUST == True and scrollDisplace != 0 and inputType == 'mouse': + + # get some variables. + ctrl = bool(op('null_mod')['ctrl']) + shift = bool(op('null_mod')['shift']) + + # if user scrolls, assume they are either trying to scroll vertically, or adjust a slider. + # so lets make sure all our auxiliary UI are closed. + op('field').Close() + op('menu').Close() + op('colorpicker').Close() + op('tooltip').Close() + + # if scroll timer is running, re-cue it. + if self.scrollTimer['running'] == 1: + self.scrollTimer.par.cuepulse.pulse() + + # if scroll timer has ended, get the current parameter values + # and store them for now as latest, and restart scrolltimer. + else: + ValueState = list(map(str,self.paramInfo.col('value'))) + self.ownerComp.store('ValueState' , ValueState) + self.scrollTimer.par.start.pulse() + + # parse our webRenderPick dat. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # make sure our renderpick data is valid. + if pikDict != None: + + # get the parameter we're hovering over. + initParName = str( pikDict["Par"] ) + + # if user scrolled on a label, proceed. + if initParName.endswith('_l'): + + # chop off suffix, and keep prefix + initParName = initParName.replace('_l', '') + + # if user scrolled on an actual paramter slider, proceed. + else: + + # get some relevant info, numeric range, style, and menu names if it exists. + normMin = float( self.paramInfo[initParName,'normmin'] or 0 ) + normMax = float( self.paramInfo[initParName,'normmax'] or 0 ) + style = str( self.paramInfo[initParName,'style'] ) + try: + menunames = eval( str(self.paramInfo[initParName,'menunames']) ) + except: + menunames = [] + + # scale down the default StepSize by factor of 50. + # TODO: make this default more meaningful or parameter specific? + normStepSize = (normMax - normMin) / 50 + + # init some lists. + initPars = [] + initVals = [] + + # iterate through all of the destination operators. + for thisOp in self.dstOpsDat.rows(): + thisOp = op(thisOp[0]) + + # if operator exists, get the parameter we're trying to adjust. + if thisOp != None: + foundPar = getattr( thisOp.par , initParName , ":PAR_ERR:" ) + else: + foundPar = ":PAR_ERR:" + + + # now, if that parameter exists too... proceed. + if foundPar != ":PAR_ERR:": + + # if we're scrolling pretty much any float/numeric parameter we handle it like this. + if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'WH' , 'XY' , 'XYZ' ]: + + # for shift, we scroll more, x10 + if shift: + increment = scrollDisplace * normStepSize * 10 + + # for ctrl, we scroll less, /10 + elif ctrl: + increment = scrollDisplace * normStepSize * .1 + + # else if no modifiers, we just scroll the normal amount. + else: + increment = scrollDisplace * normStepSize + + # get the current value of the par right now. + curVal = foundPar.eval() + + # add these to the initpar list and initval list. + initPars += [ foundPar ] + initVals += [ curVal ] + + # calculate the new value, current plus increment + newVal = curVal + increment + foundPar.val = newVal + + # scrolling ints are a bit different. + if style in [ 'Int' ]: + + # we mult our increments by whole numbers, only up. + # this usually works well as integers are not usually spatial, + # and thus smaller increments makes sense. + if shift: + increment = scrollDisplace * 10 + elif ctrl: + increment = scrollDisplace * 2 + else: + increment = scrollDisplace * 1 + + # get the current value of the par right now, increment, and set. + curVal = foundPar.eval() + newVal = curVal + increment + foundPar.val = newVal + + # scrolling menus are similar to ints, but no modifiers are used. the increment is always factor of 1. + elif style in [ 'Menu' ]: + increment = scrollDisplace + curVal = foundPar.eval() + foundPar.menuIndex = tdu.clamp( foundPar.menuIndex + increment , 0 , len(menunames)-1 ) + + # scrolling strmenus are similar, but a bit more annoying since the str may not exist in the menuNames list. + # so we have some extra try/catch logic to reset things to the first item, if there are no valid ones. + elif style in [ 'StrMenu' ]: + increment = scrollDisplace + curVal = foundPar.eval() + if curVal == '': + curVal = 0 + try: + curVal = int(curVal) + chosemMenuName = menunames[curVal] + except: + chosemMenuName = curVal + + curMenuIndex = menunames.index(chosemMenuName) + newIndex = tdu.clamp( curMenuIndex + increment , 0 , len(menunames)-1 ) + + foundPar.val = menunames[newIndex] + + # store the latest pars and values. + self.ownerComp.store('initPar', initPars) + self.ownerComp.store('initVal', initVals) + + return + @@ -258,7 +1094,138 @@ def Set_DragOverlay(self, webRenderTop , state=0 , elementID='' ): # print(state,) script = "Set_DragOverlay_('{0}','{1}')".format(state,elementID) webRenderTop.executeJavaScript(script) - + + + def Trigger_DelayedScrollChange( self ): + # this timer chop gives us a slight delay before triggering the parameter changed script. + ValueStateDown = self.ownerComp.fetch('ValueState') + ValueStateUp = list(map(str,self.paramInfo.col('value'))) + if (ValueStateUp != ValueStateDown): + self.ownerComp.ParamChange( + pars=self.ownerComp.fetch("initPar" , []) , + prevVals=self.ownerComp.fetch("initVal" , []) , + ) + return + + + def Trigger_DelayedDoubleClick( self ): + ''' + This is called when user doubleclicks anywhere in the UI. Most places do not have + doubleclick functionality, but if the user double clicks in certain types of parameters + like a numeric value field, this will launch the field, and they can enter a value by hand. + ''' + + # Get the html title, which contains the 'WebRenderPick' custom data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # get the initial par name. + initParName = str( pikDict.get("Par","") ) + + # nothing happens if the user dbl clicks on a label. + if initParName.endswith('_l'): + pass + + # nothing happens if the user dbl clicks on a tooltip. + elif initParName.endswith('_tt'): + pass + + # this is a troubleshooting branch. this message shouldn't happen, but if it does indicates where the problem lies. + elif initParName == '_dragOverlayRight_': + debug('Couldnt launch UI for %s'%(initParName)) + + # if we're here, it's assumed the user dbl clicked on an actual parameter. + else: + + # if param and pik is valid... + if pikDict != None and initParName != "": + + # get some info about the parameter user dbl clickedo n. + left = float( pikDict["left"] ) + right = float( pikDict["right"] ) + top = float( pikDict["top"] ) + bottom = float( pikDict["bottom"] ) + initParName = str( pikDict["Par"] ) + style = str( self.paramInfo[initParName,'style'] ) + + # only proceed if the parameter style is one that supports field entry. + if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , 'Momentary' ]: + + # get all the destination objects. + dstOps = [ x.path for x in map(op,self.dstOpsDat.col(0)) if x != None ] + + # if there is at least one destination object, Launch the field. + if len(dstOps): + op('field').Launch_Delayed( dstOps , initParName , left , right , bottom , top , 0 ) + + return + + + def Set_TabNextTarget( self ): + ''' + handles seeking out the next field to tab to, when called. + ''' + + # if button state was True, and the user currently has focus in the UberGui panel.. + if self.ownerComp.panel.focusselect == 1: + + # if they haven't actually submitted the value from the field previously, do so for them now. + op('field').Set() + + # parse and fetch the next parameter name from the webRenderPick dat. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + nextParName = str(pikDict['Par2']) + + # set the next parameter in the webrender using the mouse function. + # this executes some javascript that establishes the next param as current + # so the user can keep hitting tab to iterate through many. + self.Mouse( self.webRenderTop , x=0 , y=0 , targetPar=nextParName ) + return + + def Trigger_TabNextTarget( self ): + ''' + When user hits the tab key, the field will shift to the next available valid parameter + allowing rapid fire entry across multiple parameters. This has to be triggered with a delay, + hence living in a separate text file like this. + ''' + + # parse webRenderPick data. + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + # get some information about the current parameter. + initParName = str( pikDict["Par"] ) + left = float( pikDict["left"] ) + right = float( pikDict["right"] ) + top = float( pikDict["top"] ) + bottom = float( pikDict["bottom"] ) + style = str( self.paramInfo[initParName,'style'] ) + + # user can't use field entry for these, so we avoid them. + if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , ]: + + # launch the field. + dstPaths = [ x[0].val for x in self.dstOpsDat.rows() ] + op('field').Launch( dstPaths , initParName , left , right , bottom , top ) + + return + + def Trigger_Escape( self ): + ''' + When user hits the escape key + ''' + op('field').Close() + return + + def Trigger_Enter( self ): + ''' + When user hits the enter key + ''' + op('field').Set() + op('field').Close() + return + def Launch(self, srcOp , DstOps): ''' @@ -279,8 +1246,6 @@ def Clear(self): def IsStringFloat(self, arg_ = ''): - - # hasDecimal = True if str(arg_).find('.') != -1 else False try: float(arg_) isFloat = True @@ -293,6 +1258,57 @@ def IsStringFloat(self, arg_ = ''): return True else: return False + + def GetStyleOfCurrent( self ): + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + style = str( self.paramInfo[pikDict['Par'],'style'] ) + return style + + def IsPickingDataValid( self ): + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + if pikDict['Par'] == 'INIT': + return False + else: + return True + + def IsHoveredParamFieldCompatible( self ): + + pikStr = self.webInfo['title',1].val + pikDict = self.ParseTitle(pikStr) + + initParName = str( pikDict["Par"] ) + style = str( self.paramInfo[initParName,'style'] ) + + # print(style) + return style + + def SetScrollContext(self, uVal): + # given a normalized U val, we determine if our mouse wheel context is scroll,or adjust. + uSplitVal = self.ownerComp.par.Labelvaluesplit.eval() + Wheelcontext = self.ownerComp.par.Wheelcontext.eval() + ScrollbarFractional = 1-(self.ownerComp.par.Scrollbarwidth.eval() / self.ownerComp.width) + + # if the uVal is above ScrollbarFractional, our mouse is inside the scrollbar on the right. + if (uVal > ScrollbarFractional): + if Wheelcontext != 'scroll': + self.ownerComp.par.Wheelcontext = 'scroll' + + # if the above fails, we know the mouse is to the left of scrollbar. so we just check if it's right of split. + elif (uVal > uSplitVal): + if Wheelcontext != 'adjust': + self.ownerComp.par.Wheelcontext = 'adjust' + + # if above fails, we know the only remaining option is that it's in the left side, amongst the labels. + else: + if Wheelcontext != 'scroll': + self.ownerComp.par.Wheelcontext = 'scroll' + return + + def GetScrollContext(self): + # get mouse wheel context + return self.ownerComp.par.Wheelcontext.eval() def String_Numeric_Parse(self, arg_ = ''): @@ -348,4 +1364,11 @@ def ParamChange(self, pars=[], prevVals=[]): paramChangeDat.run(pars,prevVals) else: debug('A parameter has been changed..') - \ No newline at end of file + + def SetMode_Mouse(self): + if self.ownerComp.par.Inputmode.eval() != 'mouse': + self.ownerComp.par.Inputmode = 'mouse' + + def SetMode_Touch(self): + if self.ownerComp.par.Inputmode.eval() != 'touch': + self.ownerComp.par.Inputmode = 'touch' \ No newline at end of file diff --git a/python/chopexec_hover.py b/python/chopexec_hover.py deleted file mode 100644 index 830817f..0000000 --- a/python/chopexec_hover.py +++ /dev/null @@ -1,41 +0,0 @@ -##### This script will take the ambient user interaction, like mouse movement/position -##### and send this into the web render top as just mouse position. This is important because -##### when we eventually do click something, we want to already know what the mouse is over. -##### It is also important for hover effects and things like that. - - -# define references to some operators. -uv = op('null_uv_inside') -webTop = op('WEB_RENDER') -chopexec_lselect = op('chopexec_lselect') - -def whileOn(channel, sampleIndex, val, prev): - - # get u position as clamped 0-1 - u = float(uv['u']) - u = tdu.clamp( u , 0 , 1 ) - - # find out if mouse is in the right side, or left side. - isInRightSide = int(u >= .5 and u <= 1.0) - - # get the initParName from the left select script dat storage. - initParName = chopexec_lselect.fetch("initParNAME" , '') - forcedParName = ['',initParName][ min(int(uv['select']) , isInRightSide) ] - - # calculate the X and Y position in absolute pixel values. This is what the render top wants. - x = int(u * parent.Widget.width) - x = tdu.clamp(x , 0 , parent.Widget.width) - y = int( parent.Widget.height - int(uv['v'] * parent.Widget.height) ) - - # send the mouse position, and last initialized par name to Mouse function. - parent.Widget.Mouse( webTop, x , y , forcedParName ) - - return - - -def onOnToOff(channel, sampleIndex, val, prev): - - # clear the interaction by hovering to pixel 0,0 when mouse leaves container. - webTop.interactMouse( 0 , 0 , pixels=True ) - - return \ No newline at end of file diff --git a/python/chopexec_lselect.py b/python/chopexec_lselect.py deleted file mode 100644 index d88c190..0000000 --- a/python/chopexec_lselect.py +++ /dev/null @@ -1,422 +0,0 @@ -##### This script is the primary driver for most of the UI's functionality with the mouse and left clicking. -##### It can launch menu pickers, drag sliders, reset parameters, open tooltips, etc. - -# define some operator references. -uv = op('null_uv_lselect') -webTop = op('WEB_RENDER') -webInfo = op('WEB_INFO') -tupleLookup = op('null_tupleLookup') -paramInfo = op('null_paramInfo') -dstOpsDat = op('null_dstOps') - - -# OffToOn is the "DownClick", where we calculate a bunch of things, and do the hard work so -# the whileOn function can operate as quickly as possibly every frame down below. -# we use node storage on this node to store these initial values. -def onOffToOn(channel, sampleIndex, val, prev): - - # Get the html title, which contains the 'WebRenderPick' custom data. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - me.store("pikDict" , pikDict) - - # If the user left clicked somewhere, we can be sure the auxilary UI stuffs should be closed, if not already. - op('field').Close() - op('menu').Close() - op('colorpicker').Close() - op('tooltip').Close() - - # first lets make sure our pikDict exists... - if pikDict != None: - - # If it does, extract our mouse position, bottom pixel coord, and hovered element/Par name. - initX = float( pikDict["X"] ) - initY = float( pikDict["Y"] ) - bottom = float( pikDict["bottom"] ) - initParName = str( pikDict["Par"] ) - - # we only have 1 source object feeding our UI generation, but we could be addressing - # the parameters of multiple objects ! get those object(s) now and store them. - dstOps = [ x[0].val for x in dstOpsDat.rows() ] - me.store("initOps" , dstOps) - - # get and store a list of the values of all the parameters in our source object. - ValueState = list(map(str,paramInfo.col('value'))) - parent.Widget.store('ValueState' , ValueState) - - # now that our data is prepared, lets handle our numerous edge cases. The user might - # have their mouse over a label, or maybe a spacer. They might also have it over a slider, etc. - # Edge Case #1 : Labels. - if initParName.endswith('_l'): # is a label. - - # take off the suffix, now that we are in label branch. - initParName = initParName.replace('_l', '') - - # get the first matching parameter name, of the tuple. - # A parameter even if a single value param, is always part of a Tuple, so we can assume length of >= 1 - initParName = [ x.offset(0,1).val for x in tupleLookup.findCells(initParName , cols=[0]) ][0] - - # list comp convert our destination operators to a list of destimation parameters, then store it. - foundPars = [ getattr( op(x).par , initParName , ":PAR_ERR:" ) for x in dstOps ] - me.store("initPar" , foundPars) - - # determine the style of the parameter, and store it. - style = paramInfo[initParName,'style'] - me.store("style" , style) - - # Edge Case #2 : Spacers - not much to do here. - elif initParName == '_spacer_': # is a spacer - - me.store("parCick" , 0) - me.store("style" , '') - - # Edge Case #3 : Tool Tip. - elif initParName.endswith('_tt'): # is a tooltip. - - # take off the suffix, now that we are in this branch. - toolTipName = initParName.replace('_tt', '') - - # get the first destination op, as the src for the tooltop. - srcOp = op(dstOps[0]) - - # if object exists, proceed. - if srcOp != None: - - # get the ubergui config override dat. our tooltip should be in here. - configFile = srcOp.op('Uberguiconfigoverride') - - # if the config file exists, proceed. NOTE: it doesn't have to exist. - if configFile != None: - - # if it does exist, get the matching cell, which is the tooltip text. - toolTip = configFile[ toolTipName , 1 ] - - # if the cell exists, we can launch the tooltip! - # NOTE: it doesn't have to exist. - if toolTip != None: - op('tooltip').Launch( BOTTOM=bottom , MSG=toolTip.val ) - - # Edge Case #4 : File or Folder Chooser. - elif initParName.endswith('_fp'): # file or folder chooser. - - # take off the suffix, now that we are in this branch., get the param style. - initParName = initParName.replace('_fp','') - style = paramInfo[initParName,'style'] - - # sub branch: - if style == "File": # if file, launch file chooser. - path = ui.chooseFile(title="choose a file.") - - elif style == "Folder": # if folder, launch folder chooser. - path = ui.chooseFolder(title="choose a folder.") - - # for each of the destination objects, set the new path to the matching parameter - for each in dstOpsDat.rows(): - t = op(each[0]) - if t != None: - foundPar = getattr( t.par , initParName , ":PAR_ERR:" ) - else: - foundPar = ":PAR_ERR:" - if foundPar != ":PAR_ERR:": - foundPar.val = path - - # Edge Case #5 : Menu Picker. - elif initParName.endswith('_mp'): # menu picker - - # take off the suffix, now that we are in this branch. - initParName = initParName.replace('_mp','') - - # get some dimensions of our UI, so we can intelligently overlay our menu picker! - left = parent.Widget.width/2 - right = float( pikDict["right"] ) - top = float( pikDict["top"] ) - bottom = float( pikDict["bottom"] ) - style = str( paramInfo[initParName,'style'] ) - - # launch menu picker. - op('menu').Launch( dstOps , initParName , left , right , bottom , top ) - - # Edge Case #6 : Color Picker. - elif initParName.endswith('_cp'): # color picker - - # take off the suffix, now that we are in this branch. - initParName = initParName.replace('_cp','') - - # get some dimensions of our UI, so we can intelligently overlay our color picker! - left = parent.Widget.width/2 - right = float( pikDict["right"] ) - top = float( pikDict["top"] ) - bottom = float( pikDict["bottom"] ) - style = str( paramInfo[initParName,'style'] ) - - # launch color picker. - op('colorpicker').Launch( dstOps , initParName , left , right , bottom , top ) - - # Edge Case #7 : Everything else IE. widget parameter sliders! - else: # is a widget. - - # store an indicator that we clicked an actual par, for later. - me.store("parCick" , 1) - - # if init par existed, fetch style, and normmin and normmax from the paramInfo table. - if paramInfo[initParName,'style'] != None: - style = str( paramInfo[initParName,'style'] ) - normMin = float( paramInfo[initParName,'normmin'] ) - normMax = float( paramInfo[initParName,'normmax'] ) - - # Otherwise, just nullify the variables. - else: - style = '' - normMin = 0 - normMax = 0 - - # try to get menu item list as a python list, if it fails, it's because it's not a menu param. - try: - menunames = eval( str(paramInfo[initParName,'menunames']) ) - except: - menunames = [] - - # using list comp, filter our destination operators down to ones that are real. - foundDstOps = [ op(x) for x in dstOps if op(x) != None ] - - # list comp convert our dest ops to list of actual parameters. - foundPars = [ getattr( dstOp.par , initParName , ":PAR_ERR:" ) for dstOp in foundDstOps ] - - # filter out pars that did not exist on some objects. - foundPars = [ each for each in foundPars if each != ":PAR_ERR:" ] - - # store some initial x/y position values, and our parameter list. - # storing these paramters now means we don't have to re-look them up every frame during a drag. more efficient! - me.store("initX" , initX) - me.store("initY" , initY) - me.store("initPar" , foundPars) - - # we also need the initial value of the parameter, so we know how much relative, we've adjusted the value. - me.store('initVal' , [each.eval() for each in foundPars]) - - # we also need some other attributes of our parameter during our drag operations, store those too. - me.store("normMin" , normMin) - me.store("normMax" , normMax) - me.store("style" , style) - me.store("menunames" , menunames) - me.store("initParNAME" , initParName) - - # if the parameter style is one that can have a slider graphic, we need to set our DragOverlay to mask our active parameter. - if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'XY' , 'XYZ' , 'WH' , 'Int' , 'Menu' , 'StrMenu' ]: - parent.Widget.Set_DragOverlay( webTop , 1 , initParName ) - - # if the parameter style is one that is a button like par, that can have a state, we also want to enable the DragOverlay mask, - # but really just so we're not dragging/affecting other params accidentally during a rushed drag/click. - elif style in [ 'Momentary' , 'Toggle' ]: - parent.Widget.Set_DragOverlay( webTop , 1 , initParName ) - for thisPar in foundPars: - if thisPar != ":PAR_ERR:": - - # just invert the buttons state on this down click. - thisPar.val = 1 - thisPar.eval() - - # if the parameter style is a pulse param, there is no state, but we still want to enable the DragOverlay mask. - # so we're not dragging/affecting other params accidentally during a rushed drag/click. - elif style in [ 'Pulse' ]: - - parent.Widget.Set_DragOverlay( webTop , 1 , initParName ) - for thisPar in foundPars: - if thisPar != ":PAR_ERR:": - - - # just pulse the parameter, it's the only thing we can do with these types. - thisPar.pulse() - - return - -# now that we have done the hard work and stored some initial values, we can deal with our user's dragging actions performed -# while the left mouse button is pressed down! -def whileOn(channel, sampleIndex, val, prev): - - # Get the html title, which contains the 'WebRenderPick' custom data. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - - # ensure we're dealing with valid webRenderPick data! - if pikDict != None: - - # get the initial par name, and the dragX position from the renderpick. - initParName = str( pikDict["Par"] ) - dragX = float( pikDict["X"] ) - - # determine if user has dragged mouse any at all. - # a careful unmoving click is different logic than a moving drag. - uDist = abs(uv['u'] - uv['rollu']) - vDist = abs(uv['v'] - uv['rollv']) - hasUserDraggedMouse = max(uDist,vDist) != 0 - - # if user is dragging over a label, that is fine. we just want the prefix. - if initParName.endswith('_l'): - initParName = initParName.replace('_l', '') - - # if user is dragging over a tooltip, that is fine. we just want the prefix. - elif initParName.endswith('_tt'): - initParName = initParName.replace('_tt', '') - - # else, assume user is dragging over the parameter slider. - else: - pars = me.fetch("initPar" , []) - - # only proceed if we have at least 1 par. Should only have one anyways. - if len(pars): - - # get the first par. - par = pars[0] - - # make sure our pick data and par are valid. - if pikStr != "" and par != ":PAR_ERR:": - - # fetch a bunch of our initial data stored during down click. - initX = me.fetch("initX" , 0) - initY = me.fetch("initY" , 0) - normMin = me.fetch("normMin" , 0) - normMax = me.fetch("normMax" , 1) - style = me.fetch("style" , '') - menunames = me.fetch("menunames" , []) - - # only proceed with this branch IF user has dragged mouse away from initial position. - if hasUserDraggedMouse: - - # Edge Case #1 : any standard float value slider. We just remap from 0-1 to normMin-normMax - if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'XY' , 'XYZ' , 'WH' ]: - newVal = tdu.remap( dragX , 0 , 1 , normMin , normMax ) - - # set this value to all the pars of the selected objects. - for each in pars: - if each != ":PAR_ERR:": - each.val = newVal - - # Edge Case #2 : any standard int value slider. We just remap from 0-1 to normMin-normMax, but then round it too. - elif style in [ 'Int' ]: - newVal = tdu.remap( dragX , 0 , 1 , normMin , normMax ) - newVal = round(newVal) - - # set this value to all the pars of the selected objects. - for each in pars: - if each != ":PAR_ERR:": - each.val = newVal - - # Edge Case #3 : any fixed Menu param. We still remap from 0-1 to normMin-normMax, and round. - elif style in [ 'Menu' ]: - newVal = tdu.remap( dragX , 0 , 1 , 0 , len(menunames)-1 ) - newVal = int( round(newVal) ) - - # thanks to menuIndex member, we can set menu's like an integer parameter. - for each in pars: - if each != ":PAR_ERR:": - each.menuIndex = newVal - - # Edge Case #4 : any StrMenu param. We will set this the same as above, but leave this branch here - # incase we want to add specifics to the StrMenu logic later. - elif style in [ 'StrMenu' ]: - - newVal = tdu.remap( dragX , 0 , 1 , 0 , len(menunames)-1 ) - newVal = int( round(newVal) ) - - # thanks to menuIndex member, we can set menu's like an integer parameter. - for each in pars: - if each != ":PAR_ERR:": - each.menuIndex = newVal - - return - - -# Most of the work is done, but the up click still has some logic stuffs that needs to happen. -def onOnToOff(channel, sampleIndex, val, prev): - - # Get the html title, which contains the 'WebRenderPick' custom data. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - - # fetch some of the things we stored on the down click. - pars = me.fetch("initPar" , []) - initVals = me.fetch("initVal" , []) - dstOps = me.fetch("initOps" , []) - ValueStateDown = parent.Widget.fetch('ValueState') - - # get the current state of the parameter. - ValueStateUp = list(map(str,paramInfo.col('value'))) - - # we want to call the Parameter Changed script, - # but should check to make sure the value has actually changed. - if (ValueStateUp != ValueStateDown): - parent.Widget.ParamChange( pars=pars, prevVals=initVals ) - - # if we had a par.. - if len(pars) > 0: - par = pars[0] - - # if our picking info was valid. - if pikDict != None: - - # get the initial param name. - initParName = str( pikDict["Par"] ) - - # If the par name is not the Initial value we get after force restart ... - if initParName != "INIT": - - # get the current X/Y and fetch style from storage. - initX = float( pikDict["X"] ) - initY = float( pikDict["Y"] ) - style = me.fetch("style" , '') - - # we also want the NEXT par in line, as well as it's bounds incase user tabs. - initParName2 = str( pikDict["Par2"] ) - left = float( pikDict["left2"] ) - right = float( pikDict["right2"] ) - top = float( pikDict["top2"] ) - bottom = float( pikDict["bottom2"] ) - - # if the user is finishing a left click on a label, this means we want to launch the field for that parameter. - if initParName.endswith('_l'): - - # of course, some parameter types don't have field support, lets make sure it's not one of those. - if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , 'Momentary' ]: - - # chop off the suffix, and keep the prefix. - parTupletName = initParName.replace('_l','') - - # find the first parameter in the tuple, that the label matches. That's where we want to launch the field. - matchingCells = [ x for x in tupleLookup.findCells(parTupletName , cols=[0]) ] - matchingCells = [ x.offset(0,1).val for x in matchingCells ] - initParName = matchingCells[0] - - # Launch the field. - op('field').Launch( dstOps , initParName , left , right , bottom , top ) - parent.Widget.Mouse( webTop , x=0 , y=0 , targetPar=initParName2 ) - - # If user finished a left click on a tooltip... do nothing (for now). place holder for later - elif initParName.endswith('_tt'): - pass - - # If user finished a left click on anything else... - else: - - # if the finished click was a momentary button, be sure to set the value back to 0. - # momentary buttons are unique in this way, easier to make a special catch here than - # to change the logic more generally. - if style in [ 'Momentary' , ]: - par = me.fetch("initPar" , ":PAR_ERR:") - if par != ":PAR_ERR:": - par.val = 0 - - # if the user finshed the left click for a Pulse, we can now call our param change script. - # we needed to wait a bit to give the Pulse time to trigger whatever it triggered. - elif style in [ 'Pulse' , ]: - parent.Widget.ParamChange( pars=pars, prevVals=initVals ) - - # if we get this from the gui, we should not act, we're probably setting enable flags. - else: - pass - - # finally, but only if our initial parameter was valid, set our DragOverlay mask back to off. - if par != ":PAR_ERR:": - initParName = par.name - parent.Widget.Set_DragOverlay( webTop , 0 , initParName ) - - return \ No newline at end of file diff --git a/python/chopexec_rselect.py b/python/chopexec_rselect.py deleted file mode 100644 index 13a7a53..0000000 --- a/python/chopexec_rselect.py +++ /dev/null @@ -1,77 +0,0 @@ -##### Similar to chopexec_lselect, however there is a lot less that happens on right click down and up. -##### So, this script will be much shorter. Really, the only thing we are handling here is the resetting of parameter values. - -# define some operator references. -uv = op('null_uv_lselect') -webTop = op('WEB_RENDER') -webInfo = op('WEB_INFO') -tupleLookup = op('null_tupleLookup') -paramInfo = op('null_paramInfo') -dstOpsDat = op('null_dstOps') - - -def onOffToOn(channel, sampleIndex, val, prev): - - # get list of all current values and store. - ValueState = list(map(str,paramInfo.col('value'))) - me.store('ValueState' , ValueState) - - # get the webRenderPick data. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - - # init some lists. - InitPars = [] - InitVals = [] - - # if picking data is valid. - if pikDict != None: - - # calc the initial par name. - initParName = str( pikDict["Par"] ) - - # Edge Case #1 : User Right Clicked a label. - if initParName.endswith('_l'): - - # Chop off suffix, and keep prefix - initParName = initParName.replace('_l', '') - - # do a tuplet lookup, and get all params in that tuplet row. - foundCells = tupleLookup.findCells(initParName, cols=['tupletname']) - desiredParNames = [x.offset(0,1).val for x in foundCells] - - # now iterate through that tuplet row. - for thisOp in dstOpsDat.rows(): - thisOp = op(thisOp[0]) - - # for each param, reset to default. - for parName in desiredParNames: - - foundParam = getattr( thisOp.par , parName , ':PAR_ERR:' ) - if foundParam != ':PAR_ERR:': - InitVals += [ foundParam.eval() ] - foundParam.val = foundParam.default - InitPars += [ foundParam ] - - # store init pars and vals - me.store('InitPars', InitPars) - me.store('InitVals', InitVals) - - return - -# nothing happens currently while right click is held down. -def whileOn(channel, sampleIndex, val, prev): - return - -# on right click release, we check to see if our values changed after the reset to default action. -# if they did, they we want to trigger the param changed script. -def onOnToOff(channel, sampleIndex, val, prev): - - ValueStateDown = me.fetch('ValueState') - InitPars = me.fetch('InitPars') - InitVals = me.fetch('InitVals') - ValueStateUp = list(map(str,paramInfo.col('value'))) - if (ValueStateUp != ValueStateDown): - parent.Widget.ParamChange(pars=InitPars, prevVals=InitVals) - - return \ No newline at end of file diff --git a/python/datexec_UPDATE.py b/python/datexec_UPDATE.py deleted file mode 100644 index bf447ce..0000000 --- a/python/datexec_UPDATE.py +++ /dev/null @@ -1,117 +0,0 @@ -##### This script monitors lots of deeper parameter data from the source object, and when any of it changes, -##### it attempts to re-calculate the displayed value, and slider position if applicable. The parameter dat really -##### makes this process awesome, because it outputs min/max settings, section, menu names, etc which can alter -##### the range of a slider for instance. - - -def onRowChange(dat, rows): - # define some things. - SRC = op(op('null_srcOp')[0,0]) - uberGuiOverrideDat = SRC.op('Uberguiconfigoverride') - data = [] - - # iterate over all the table rows that changed.. - for rowID in rows: - - # collect some preliminary data bout the changed row/parameter. - style = dat[rowID,'style'].val - name = dat[rowID,'name'].val - tupletname = dat[rowID,'tupletname'].val - - # just placeholders, we'll fill these in down below. - value = '' - slide = 0 - - # Edge Case #1 : menu params. We want to display - # the selected item's menu label. so some lookup stuff happens for that. - # then we calc the slider position based on the index of the chosen item. - if style in [ 'Menu' , 'StrMenu' ]: - menuindex = int(dat[rowID,'menuindex']) - menulabels = dat[rowID,'menulabels'].val - menulabels = eval(menulabels) - try: # using try/except so that we can silently fail for StrMenu types. - value = menulabels[menuindex] - slide = tdu.remap( menuindex , 0 , len(menulabels)-1 , 0 , 1 ) - except: # if user puts in incorrect string to StrMenu, we just set to 0/blank. - value = '' - slide = 0 - - # Edge Case #2 : buttons that can have state changes. - # conveniently, the param DAT outputs menu items ['off', 'on'] even for buttons, so - # we can take advantage of that for a value to show, and set our slider to 0% or 100% - elif style in [ 'Momentary' , 'Toggle' ]: - - menuindex = int(dat[rowID,'value']) - menulabels = dat[rowID,'menulabels'].val - menulabels = eval(menulabels) - value = menulabels[menuindex] - slide = menuindex - - - # Edge Case #3 : pulse buttons don't really have states, they just trigger things instantly. - # So for these we want the slider to always be 0%, and the value to just be the param label. - # this encourages developers to name the pulse button's label something action oriented, - # for instance "Open Window" or "Start Render", etc. - elif style in [ 'Pulse' ]: - - value = dat[rowID,'label'].val - slide = 0 - - # Ok done with edge cases, we are able to process all other parameter types under this next logical branch! - else: - - # get the custom user provided format of the parameter, if there is any. - if uberGuiOverrideDat != None: - CustomFormat = uberGuiOverrideDat[tupletname,'style'].val if uberGuiOverrideDat[tupletname,'style'] != None else None - CustomFormat = CustomFormat if CustomFormat != '' else None - else: - CustomFormat = None - - # If no custom format was specified, do the default thing. - if CustomFormat == None: - - # try to treat it numerically, but if it fails, it's probably string based. - try: - value = float(dat[rowID,'value']) - value = int(float(value) * 1000) / 1000 - normmin = float(dat[rowID,'normmin']) - normmax = float(dat[rowID,'normmax']) - slide = tdu.remap( value , normmin , normmax , 0 , 1 ) - - # assuming it's a string. - except: - value = dat[rowID,'value'].val - slide = 0 - - # one of the custom ubergui override formats is Rgba255. - # This essentially allows you to work with color values as integers, in the range of 0-255. - elif CustomFormat in [ 'Rgba255' ]: - - # try to treat it numerically, but if it fails, it's probably string based. - try: - value = float(dat[rowID,'value']) - normmin = float(dat[rowID,'normmin']) - normmax = float(dat[rowID,'normmax']) - slide = tdu.remap( value , normmin , normmax , 0 , 1 ) - value = int(round(value * 255)) - - # assuming it's a string. - except: - value = dat[rowID,'value'].val - slide = 0 - - # if CustomFormat is spaceToUnderscore or tduLegal this means we are working with a name parameter - # or something like that, where we cannot have spaces. Since this script is dealing with display only values - # it's assumed the string is already formatted correctly and this is just placeholder incase we need to - # do more with the display side of this edge case. - elif CustomFormat in [ 'spaceToUnderscore' , 'tduLegal' ]: - value = dat[rowID,'value'].val - slide = 0 - - # add the changed names, value, and slider pos to our data list - data += [[ name , value , slide ]] - - # Update those changes to the web render top. - parent.Widget.Update( op('WEB_RENDER') , data ) - - return \ No newline at end of file diff --git a/python/dblClick_delayed.py b/python/dblClick_delayed.py deleted file mode 100644 index d5473fe..0000000 --- a/python/dblClick_delayed.py +++ /dev/null @@ -1,55 +0,0 @@ -##### This is called when user doubleclicks anywhere in the UI. Most places do not have -##### doubleclick functionality, but if the user double clicks in certain types of parameters -##### like a numeric value field, this will launch the field, and they can enter a value by hand. - - -# define some operator references. -webInfo = op('WEB_INFO') -srcOpDat = op('null_srcOp') -dstOpsDat = op('null_dstOps') -paramInfo = op('null_paramInfo') - -# parse webRenderPick data. -pikStr = webInfo['title',1].val -pikDict = op('chopexec_lselect').fetch("pikDict",{}) - -# get the initial par name. -initParName = str( pikDict.get("Par","") ) - -# nothing happens if the user dbl clicks on a label. -if initParName.endswith('_l'): - pass - -# nothing happens if the user dbl clicks on a tooltip. -elif initParName.endswith('_tt'): - pass - -# this is a troubleshooting branch. this message shouldn't happen, but if it does indicates where the problem lies. -elif initParName == '_dragOverlayRight_': - debug('Couldnt launch UI for %s'%(initParName)) - -# if we're here, it's assumed the user dbl clicked on an actual parameter. -else: - - # if param and pik is valid... - if pikDict != None and initParName != "": - - # get some info about the parameter user dbl clickedo n. - left = float( pikDict["left"] ) - right = float( pikDict["right"] ) - top = float( pikDict["top"] ) - bottom = float( pikDict["bottom"] ) - initParName = str( pikDict["Par"] ) - style = str( paramInfo[initParName,'style'] ) - - # only proceed if the parameter style is one that supports field entry. - if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , 'Momentary' ]: - - # get all the destination objects. - dstOps = [ x.path for x in map(op,dstOpsDat.col(0)) if x != None ] - - # if there is at least one destination object, Launch the field. - if len(dstOps): - op('field').Launch_Delayed( dstOps , initParName , left , right , bottom , top , 0 ) - -# ''' \ No newline at end of file diff --git a/python/delayed_tab.py b/python/delayed_tab.py deleted file mode 100644 index 48e9847..0000000 --- a/python/delayed_tab.py +++ /dev/null @@ -1,30 +0,0 @@ -##### When user hits the tab key, the field will shift to the next available valid parameter -##### allowing rapid fire entry across multiple parameters. This has to be triggered with a delay, -##### hence living in a separate text file like this. - - -# define some operator references. -insideChop = op('panel_uv_inside') -webRenderTop = op('WEB_RENDER') -paramInfo = op('null_paramInfo') -webInfo = op('WEB_INFO') -dstOpsDat = op('null_dstOps') - -# parse webRenderPick data. -pikStr = webInfo['title',1].val -pikDict = parent.Widget.ParseTitle(pikStr) - -# get some information about the current parameter. -initParName = str( pikDict["Par"] ) -left = float( pikDict["left"] ) -right = float( pikDict["right"] ) -top = float( pikDict["top"] ) -bottom = float( pikDict["bottom"] ) -style = str( paramInfo[initParName,'style'] ) - -# user can't use field entry for these, so we avoid them. -if style not in [ 'Menu' , 'StrMenu' , 'Pulse' , 'Toggle' , ]: - - # launch the field. - dstPaths = [ x[0].val for x in dstOpsDat.rows() ] - op('field').Launch( dstPaths , initParName , left , right , bottom , top ) \ No newline at end of file diff --git a/python/field.py b/python/field.py index 477ba8c..d213768 100644 --- a/python/field.py +++ b/python/field.py @@ -53,7 +53,6 @@ def Launch(self, OPS , PAR, LEFT, RIGHT, BOTTOM, TOP): # the value that already exists in the par. IE rounding long floats, or making sure ints look like ints, etc. if doesParExist != None: - currentVal = currentPar.val currentStyle = currentPar.style @@ -68,7 +67,7 @@ def Launch(self, OPS , PAR, LEFT, RIGHT, BOTTOM, TOP): elif currentStyle in [ 'Int' ]: currentVal = int(currentVal) - elif currentStyle in [ 'Str' , 'CHOP' , 'COMP' , 'DAT' , 'MAT' , 'panelCOMP' , 'SOP' , 'TOP' , 'Python' ]: + elif currentStyle in [ 'Str' , 'CHOP' , 'COMP' , 'DAT' , 'MAT' , 'PanelCOMP' , 'SOP' , 'TOP' , 'Python' , 'OP' , 'Folder' , 'File' , 'OBJ' ]: currentVal = currentVal else: diff --git a/python/keyboardin_hotkeys_callbacks.py b/python/keyboardin_hotkeys_callbacks.py deleted file mode 100644 index df5ad8d..0000000 --- a/python/keyboardin_hotkeys_callbacks.py +++ /dev/null @@ -1,49 +0,0 @@ -##### This script handles several keyboard hotkey functions, like tab, esc, and enter. - - - -# define some operator references. -insideChop = op('panel_uv_inside') -webRenderTop = op('WEB_RENDER') -paramInfo = op('null_paramInfo') -webInfo = op('WEB_INFO') -srcOpDat = op('null_srcOp') - -def onKey(dat, key, character, alt, lAlt, rAlt, ctrl, lCtrl, rCtrl, shift, lShift, rShift, state, time): - - # if button state was True, and the user currently has focus in the UberGui panel.. - if state and parent.Widget.panel.focusselect == 1: - - # if they pressed tab, we want to move field to next parameter. - if key == 'tab': - - # if they haven't actually submitted the value from the field previously, do so for them now. - op('field').Set() - - # parse and fetch the next parameter name from the webRenderPick dat. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - nextParName = str(pikDict['Par2']) - - # set the next parameter in the webrender using the mouse function. - # this executes some javascript that establishes the next param as current - # so the user can keep hitting tab to iterate through many. - parent.Widget.Mouse( webRenderTop , x=0 , y=0 , targetPar=nextParName ) - - # run the tab code delayed by a frame. - op('delayed_tab').run(delayFrames=1) - - # if the user hit escape, just close the current field, with out submitting the value. - if key == 'esc': - op('field').Close() - - # if they pressed enter, Set the value from the field, then close it. - if key == 'enter': - op('field').Set() - op('field').Close() - - return - -def onShortcut(dat, shortcutName, time): - return; - \ No newline at end of file diff --git a/python/panelexec_mouseScroll.py b/python/panelexec_mouseScroll.py deleted file mode 100644 index ff4578d..0000000 --- a/python/panelexec_mouseScroll.py +++ /dev/null @@ -1,171 +0,0 @@ -##### This script is run any time the user scrolls their mouse wheel, A lot of different things -##### can happen depending on what the mouse is hovering over at that time, so this has some branching -##### logic to accomodate all of that. - - -import copy - -# define some operator references. -webInfo = op('WEB_INFO') -paramInfo = op('null_paramInfo') -mod = op('null_mod') -scrollTimer = op('timer_scrollChange') -scrollTimerCallbacks = op('timer_scrollChange_callbacks') -dstOpsDat = op('null_dstOps') -delayedScrollSet = op('scrollSet_delayed') - -# any time the mouse wheel value changes, this function is called. -def onValueChange(panelValue): - - # funny thing about the mouse wheel, is if you scroll a notch, you get a non-zero value, then a zero. - # zero can obviously be calculated, but it's wasted cpu cycles since it doesn't do anything. - # this if statement only executes a scroll IF the value is one of those non-zero ones. - if panelValue != 0: - - # if user scrolls, assume they are either trying to scroll vertically, or adjust a slider. - # so lets make sure all our auxiliary UI are closed. - op('field').Close() - op('menu').Close() - op('colorpicker').Close() - op('tooltip').Close() - - # cook modifiers CHOP. Nothing else has been requesting it. - mod.cook(force=1) - shift = int(mod['shift']) - ctrl = int(mod['ctrl']) - - # if scroll timer is running, re-cue it. - if scrollTimer['running'] == 1: - scrollTimer.par.cuepulse.pulse() - - # if scroll timer has ended, get the current parameter values - # and store them for now as latest, and restart scrolltimer. - else: - ValueState = list(map(str,paramInfo.col('value'))) - parent.Widget.store('ValueState' , ValueState) - scrollTimer.par.start.pulse() - - # parse our webRenderPick dat. - pikStr = webInfo['title',1].val - pikDict = parent.Widget.ParseTitle(pikStr) - - # make sure our renderpick data is valid. - if pikDict != None: - - # get the parameter we're hovering over. - initParName = str( pikDict["Par"] ) - - # if user scrolled on a label, proceed. - if initParName.endswith('_l'): - - # chop off suffix, and keep prefix - initParName = initParName.replace('_l', '') - - # if user scrolled on an actual paramter slider, proceed. - else: - - # get some relevant info, numeric range, style, and menu names if it exists. - normMin = float( paramInfo[initParName,'normmin'] or 0 ) - normMax = float( paramInfo[initParName,'normmax'] or 0 ) - style = str( paramInfo[initParName,'style'] ) - try: - menunames = eval( str(paramInfo[initParName,'menunames']) ) - except: - menunames = [] - - # scale down the default StepSize by factor of 50. - # TODO: make this default more meaningful or parameter specific? - normStepSize = (normMax - normMin) / 50 - - # init some lists. - initPars = [] - initVals = [] - - # iterate through all of the destination operators. - for thisOp in dstOpsDat.rows(): - thisOp = op(thisOp[0]) - - # if operator exists, get the parameter we're trying to adjust. - if thisOp != None: - foundPar = getattr( thisOp.par , initParName , ":PAR_ERR:" ) - else: - foundPar = ":PAR_ERR:" - - - # now, if that parameter exists too... proceed. - if foundPar != ":PAR_ERR:": - - # if we're scrolling pretty much any float/numeric parameter we handle it like this. - if style in [ 'Float' , 'RGB' , 'RGBA' , 'UV' , 'UVW' , 'WH' , 'XY' , 'XYZ' ]: - - # for shift, we scroll more, x10 - if shift: - increment = panelValue * normStepSize * 10 - - # for ctrl, we scroll less, /10 - elif ctrl: - increment = panelValue * normStepSize * .1 - - # else if no modifiers, we just scroll the normal amount. - else: - increment = panelValue * normStepSize - - # get the current value of the par right now. - curVal = foundPar.eval() - - # add these to the initpar list and initval list. - initPars += [ foundPar ] - initVals += [ curVal ] - - # calculate the new value, current plus increment - newVal = curVal + increment - foundPar.val = newVal - - # scrolling ints are a bit different. - if style in [ 'Int' ]: - - # we mult our increments by whole numbers, only up. - # this usually works well as integers are not usually spatial, - # and thus smaller increments makes sense. - if shift: - increment = panelValue * 10 - elif ctrl: - increment = panelValue * 2 - else: - increment = panelValue * 1 - - # get the current value of the par right now, increment, and set. - curVal = foundPar.eval() - newVal = curVal + increment - foundPar.val = newVal - - # scrolling menus are similar to ints, but no modifiers are used. the increment is always factor of 1. - elif style in [ 'Menu' ]: - increment = panelValue - curVal = foundPar.eval() - foundPar.menuIndex = tdu.clamp( foundPar.menuIndex + increment , 0 , len(menunames)-1 ) - - # scrolling strmenus are similar, but a bit more annoying since the str may not exist in the menuNames list. - # so we have some extra try/catch logic to reset things to the first item, if there are no valid ones. - elif style in [ 'StrMenu' ]: - increment = panelValue - curVal = foundPar.eval() - if curVal == '': - curVal = 0 - try: - curVal = int(curVal) - chosemMenuName = menunames[curVal] - except: - chosemMenuName = curVal - - curMenuIndex = menunames.index(chosemMenuName) - newIndex = tdu.clamp( curMenuIndex + increment , 0 , len(menunames)-1 ) - - foundPar.val = menunames[newIndex] - - # store the latest pars and values. - me.store('initPar', initPars) - me.store('initVal', initVals) - - return - \ No newline at end of file