Copy link to clipboard
Copied
I have a dropdown list that is populated with the user's selection. If the user changes their selection I would like to provide a way for them to update the dropdown list to reflect that change.
In my script I currently have a button that will close the window and then rebuild it. This causes the dropdown list to be recreated with the updated selection. It's not an elegant solution but works in CS6 however when I try it in CC the entire application freezes. Any thoughts and advice on how to do this in a better way or to fix my code would be appreciated.
Here is the entire script.
| var uiPal; |
| //my variables |
| var allLayers = app.project.activeItem.layers; | ||
| var userSelection = app.project.activeItem.selectedLayers; | ||
| var selectionNames = []; | ||
| var dimension = 0; | ||
| var axisDir = 0; | ||
| for (var j=0; j<userSelection.length; j++) selectionNames | " + userSelection | |
| selectionNames.push("Average"); | ||
| var userCollection = new Array(); | ||
| var num3d = 0; | ||
| var tempPos = []; | ||
| for (var i=0; i<userSelection.length; i++) | ||
| { | ||
| userCollection.push(userSelection.position.value[dimension]); | ||
| } |
| app.endUndoGroup(); |
function buildPal(theObj){
| function buildUI(theObj){ | |||
| var myPal = (theObj instanceof Panel) ? theObj : new Window("palette", "KeyFrame Ease", undefined, {resizeable:true}); | |||
| if (myPal != null){ | |||
| var myResource = "group{orientation:'column',\ | |||
| groupOne: Group{orientation: 'row',\ | |||
| myPanelOne: Panel{text:'AXIS', orientation:'row', \ | |||
| myRadioButtonX: RadioButton{text: 'X', value: true},\ | |||
| myRadioButtonY: RadioButton{text: 'Y', value: false},\ | |||
| myRadioButtonZ: RadioButton{text: 'Z', value: false},\ | |||
| },\ | |||
| myPanelTwo: Panel{text:'AXIS direction:', orientation:'row', \ | |||
| myRadioButtonNeg: RadioButton{text: '-', value: false},\ | |||
| myRadioButtonAnch: RadioButton{text: '', value: true},\ | |||
| myRadioButtonPos: RadioButton{text: '+', value: false},\ | |||
| },\ | |||
| myPanelThree: Panel{text:'Align To:', orientation:'row', \ | |||
| myDropDownList:DropDownList{properties:{items:" + selectionNames.toSource() + "}}\ | |||
| },\ | |||
| myImage: Image{text:'Image', Image:'`/Desktop/skullIcon.jpg'},\ | |||
| },\ | |||
| groupTwo: Group{orientation:'row',\ | |||
| myButton: Button{text:'Apply', preferredSize: [50,25]},\ | |||
| myButton2: Button{text:'Refresh Selection', preferredSize: [120,25]},\ | |||
| myButton3: Button{text:'Help', preferredSize: [45,25]},\ | |||
| },\ | |||
| }"; |
| myPal.grp = myPal.add(myResource); | |||
| myPal.grp.groupOne.myPanelThree.myDropDownList.selection = selectionNames.length -1; |
| } | ||
| return myPal; | ||
| } |
| uiPal = buildUI(theObj); | |
| // Event listener for the Axis radio buttons. myRadioButtonAllerts user if not all items selected |
uiPal.grp.groupOne.myPanelOne.myRadioButtonX.onClick = uiPal.grp.groupOne.myPanelOne.myRadioButtonY.onClick = uiPal.grp.groupOne.myPanelOne.myRadioButtonZ.onClick = function () {
| if(uiPal.grp.groupOne.myPanelOne.myRadioButtonX.value) { | ||
| dimension = 0; | ||
| } | ||
| else if(uiPal.grp.groupOne.myPanelOne.myRadioButtonY.value) { | ||
| dimension = 1; | ||
| } | ||
| else if(uiPal.grp.groupOne.myPanelOne.myRadioButtonZ.value) { | ||
| for (var j=0; j<userSelection.length; j++) |
| if (userSelection | |
| if (num3d != userSelection.length){ |
| alert ("Not all selected layers are 3D! Please select only 3d layers for use with Z - Axis."); | |
| uiPal.grp.groupOne.myPanelOne.myRadioButtonX.value = true; | |
| }else{ |
| dimension = 2; | |
| return num3d = 0; |
| } | |
| } |
};
uiPal.grp.groupOne.myPanelTwo.myRadioButtonPos.onClick = uiPal.grp.groupOne.myPanelTwo.myRadioButtonAnch.onClick = uiPal.grp.groupOne.myPanelTwo.myRadioButtonNeg.onClick = function () {
| if(uiPal.grp.groupOne.myPanelTwo.myRadioButtonPos.value) { | ||
| axisDir = 2; | ||
| } | ||
| else if(uiPal.grp.groupOne.myPanelTwo.myRadioButtonAnch.value) { | ||
| axisDir = 0; | ||
| } | ||
| else if(uiPal.grp.groupOne.myPanelTwo.myRadioButtonNeg.value) { | ||
| axisDir = 1; | ||
| } |
};
//change selection dropdown variable after user selection li
var ddListSelection = "Average"
| uiPal.grp.groupOne.myPanelThree.myDropDownList.onChange = function () { | ||
| ddListSelection = uiPal.grp.groupOne.myPanelThree.myDropDownList.selection | ||
| return ddListSelection | ||
| //alert (userSelection[ddListSelection.index].position.value[dimension]); | ||
| //num = userSelection[ddListSelection.index].position.value[dimension] | ||
| } |
//apply alignment
uiPal.grp.groupTwo.myButton.onClick = function() {
app.beginUndoGroup("bt_3d_Align");
//convert dropdown object to text - contains index of selected layer
var ddIndex = ddListSelection.text
| if (ddListSelection == "Average" || ddListSelection.text == "Average") { | ||
| //create alignToAvg variable and set to average of all numbers | ||
| var alignToAvg = 0 | ||
| for (var i=0; i<userCollection.length; i++) | ||
| { | ||
| alignToAvg = alignToAvg + userCollection | ||
| } | ||
| alignToAvg = alignToAvg / userCollection.length | ||
| for (i=0;i<userSelection.length;i++) { | ||
| tempPos = userSelection.position.value; | ||
| tempPos[dimension] = alignToAvg; | ||
| if (userSelection.position.isTimeVarying == true) { | ||
| userSelection.position.setValueAtTime(userSelection.time, tempPos); | ||
| } | ||
| else { | ||
| userSelection.position.setValue(tempPos); | ||
| } | ||
| } | ||
| } |
| else { | |
| var fixPos = allLayers[ddIndex.charAt(0)].position.value[dimension]; | |
| for (i=0;i<userSelection.length;i++) { | |
| //create array to hold position value so that it may be changed | |
| var tempPos = userSelection.position.value; | |
| //place value of selected layer and dimension in to tempPos array | |
| //if else statments adjusts alignment aka axis direction | |
| if (userSelection.nullLayer || userSelection.aperture) { | |
| tempPos[dimension] = fixPos | |
| } | |
| else { | |
| if (dimension == 0 && axisDir == 2) { | |
| tempPos[dimension] = fixPos + ((userSelection.sourceRectAtTime(0,true).width / 2) - (allLayers[ddIndex.charAt(0)].sourceRectAtTime(0,true).width / 2)); | |
| } | |
| else if (dimension == 0 && axisDir == 1) { | |
| tempPos[dimension] = fixPos - ((userSelection.sourceRectAtTime(0,true).width / 2) - (allLayers[ddIndex.charAt(0)].sourceRectAtTime(0,true).width / 2)); | |
| } | |
| else if (dimension == 1 && axisDir == 2) { | |
| tempPos[dimension] = fixPos + (userSelection.sourceRectAtTime(0,true).height / 2 - (allLayers[ddIndex.charAt(0)].sourceRectAtTime(0,true).height / 2)); | |
| } | |
| else if (dimension == 1 && axisDir == 1) { | |
| tempPos[dimension] = fixPos - (userSelection.sourceRectAtTime(0,true).height / 2 - (allLayers[ddIndex.charAt(0)].sourceRectAtTime(0,true).height / 2)); | |
| } | |
| else { | |
| tempPos[dimension] = fixPos | |
| } | |
| } | |
| //if else to determin if layer has keyframes | |
| if (userSelection.position.isTimeVarying == true) { | |
| userSelection.position.setValueAtTime(userSelection.time, tempPos); | |
| //userSelection.position.setValueAtTime(userSelection.time, tempPos); | |
| } | |
| else { | |
| userSelection.position.setValue(tempPos); | |
| } |
| //adjust position based on |
| } | |
| //uiPal.close(); | |
| } |
};
app.endUndoGroup();
uiPal.grp.groupTwo.myButton3.onClick = function() {
w = new Window ("palette", undefined, undefined,);
myPanel = w.add ("panel");
myStaticText = myPanel.add ("statictext", undefined, undefined, {multiline: true});
myStaticText.size = [800, 400];
myStaticText.text = "How to use this script:\n\n1. Before running script make sure that you have a comp open and selected in the timeline. If you do not the script will warn you and you must run the script again.\n\n2. The script populates its dropdown list when first run. If you wish to change the selection of the layers you will be aligning you must press Refresh Selection.\
\n\n3. AXIS - defines the position value you will be aligning your layer items too. That is, will all layers be aligned to the same x, y, or z position.\
\n\n4. AXIS direction defines the which direction the layer will be aligned relative to the reference layer selected in the Align To: drop down. This is analagous to Left/Right justification in print or 2D AE layers.\
\n\n5. Align To: picks which layer will be used as the reference point. ie. If you wish all your selection items to have the same x position as 1_greenSolid then you would set 1_greenSolid as your reference layer.\
\n\n6. Apply runs the alignment.\
\n\n7. If you wish to change the layers that you are aligning then you must push Refresh Selection.\
\n\n8. The Average item in the Align To: dropdown takes all the position values of you selected AXIS and averages them out";
w.show ();
| } |
uiPal.grp.groupTwo.myButton2.onClick = function() {
| uiPal.close(); | ||
| allLayers = app.project.activeItem.layers; | ||
| userSelection = app.project.activeItem.selectedLayers; | ||
| selectionNames = []; | ||
| dimension = 0; | ||
| axisDir = 0; | ||
| for (var j=0; j<userSelection.length; j++) selectionNames | " + userSelection | |
| selectionNames.push("Average"); | ||
| userCollection = new Array(); | ||
| num3d = 0; | ||
| tempPos = []; | ||
| buildPal(theObj); | ||
| } |
| if (uiPal != null){ | |||
| if (uiPal instanceof Window){ | |||
| // show the palette | |||
| uiPal.center(); | |||
| uiPal.show(); | |||
| }else{ | |||
| uiPal.layout.layout(true); | |||
| } | |||
| } |
}
buildPal(this);
Hi, i rewrote your script, trying to keep the same variable names but not everywhere.
It does refresh.
You should remove the first entry of your help, a dockable script shouldnt work that way...
Xavier
...(function(theObj){
// "globals"
var uiPal, helpPal, state;
// current state
state = {
dimension : 0,
axisDir : 0,
// userSelection: the last user selection (array of layer objects)
userSelection : [],
// refIndex : the selection index in t
Copy link to clipboard
Copied
Hi Brendan,
you don't need to rebuild the whole palette to refresh a dropdown.
Just retrieve the user's selection, update the dropdownmenu and your internal variables (selectionNames, num3d, etc...)
Edit: to update a dropdown with a new array of items (newItems: array of strings), do:
var sel = myDD.selection.text, newSel;
while (myDD.items.length) myDD.remove(myDD.items[0]);
for (var n=0; n<newItems.length; n++){
newItems
if (newItems
};
myDD.selection = newSel || 0;
Xavier
Copy link to clipboard
Copied
Xavier. thanks for the help.
Any way I try to reference the dropdown list I get an error saying the name isn't recognized.
For example myPal.grp.groupOne.myPanelThree.myDropDownList.selection = selectionNames.length -1; gets an error the myPal is not recognized. I've also tried your code and swapping the myDD.selection with myDropDownList.selection. I get an another error saying myDropDownList isn't recognized even thoughI used that name to build the dropdown. I'm wondering if I'm having this problem because I built the ui with a resource string?
Copy link to clipboard
Copied
The way you declare your UI doesnt influence how it will behave afterwards. So the error is somewhere else, but where, i can't tell.
There are quite a few things that are not so good in your script, like undoGroups, or storing variables that you don't need to store (allLayers, userCollection...).
The only thing that should be stored is userSelection, all other are redundant (dimension etc are stored in the palette widgets).
But even storing the userSelection makes things quite complicated.
It would be much easier if your script worked like the built-in "align" panel: store nothing and reevaluate the user selection everytime the user wants to align something.
Otherwise you are facing possible issues with layers being deleted etc (hence not valid).
I think i would remove that userSelection, and in the dropdown put only 2 items: "average" and "this layer": if 'this layer" is selected, a group (button+static text) appears to alllow identify a layer as reference layer.
Then you only store that specific layer.
Beside all this, aligning layers in 3D is H-A-R-D ...
Xavier.
Copy link to clipboard
Copied
Hi, i rewrote your script, trying to keep the same variable names but not everywhere.
It does refresh.
You should remove the first entry of your help, a dockable script shouldnt work that way...
Xavier
(function(theObj){
// "globals"
var uiPal, helpPal, state;
// current state
state = {
dimension : 0,
axisDir : 0,
// userSelection: the last user selection (array of layer objects)
userSelection : [],
// refIndex : the selection index in the dropdown
// if refIndex>= userSelection.length : average
// else the reference layer is userSelection[refIndex]
refIndex : 0,
};
// UI : help palette;
helpPal = new Window ("palette", "KeyFrame Ease : Help", undefined, {resizeable: true});
helpPal.myPanel = helpPal.add("panel");
helpPal.myPanel.myStaticText = helpPal.myPanel.add("statictext", undefined, undefined, {multiline: true});
helpPal.myPanel.myStaticText.preferredSize = [800, 400];
helpPal.myPanel.myStaticText.text = "How to use this script:\n\n1. Before running script make sure that you have a comp open and selected in the timeline. If you do not the script will warn you and you must run the script again.\n\n2. The script populates its dropdown list when first run. If you wish to change the selection of the layers you will be aligning you must press Refresh Selection.\
\n\n3. AXIS - defines the position value you will be aligning your layer items too. That is, will all layers be aligned to the same x, y, or z position.\
\n\n4. AXIS direction defines the which direction the layer will be aligned relative to the reference layer selected in the Align To: drop down. This is analagous to Left/Right justification in print or 2D AE layers.\
\n\n5. Align To: picks which layer will be used as the reference point. ie. If you wish all your selection items to have the same x position as 1_greenSolid then you would set 1_greenSolid as your reference layer.\
\n\n6. Apply runs the alignment.\
\n\n7. If you wish to change the layers that you are aligning then you must push Refresh Selection.\
\n\n8. The Average item in the Align To: dropdown takes all the position values of you selected AXIS and averages them out";
helpPal.layout.layout(true);
// UI : main
uiPal = (theObj instanceof Panel) ? theObj : new Window("palette", "KeyFrame Ease", undefined, {resizeable:true});
uiPal.grp = uiPal.add("group{orientation:'column',\
groupOne: Group{orientation: 'row',\
myPanelOne: Panel{text:'AXIS', orientation:'row', \
myRadioButtonX: RadioButton{text: 'X', tag: 0, value: true},\
myRadioButtonY: RadioButton{text: 'Y', tag: 1, value: false},\
myRadioButtonZ: RadioButton{text: 'Z', tag: 2, value: false},\
},\
myPanelTwo: Panel{text:'AXIS direction:', orientation:'row', \
myRadioButtonNeg: RadioButton{text: '-', tag: 0, value: false},\
myRadioButtonAnch: RadioButton{text: '', tag: 1, value: true},\
myRadioButtonPos: RadioButton{text: '+', tag: 2, value: false},\
},\
myPanelThree: Panel{text:'Align To:', orientation:'row', \
myDropDownList:DropDownList{properties:{items: ['Average']}}\
},\
myImage: Image{text:'Image', Image:'`/Desktop/skullIcon.jpg'},\
},\
groupTwo: Group{orientation:'row',\
myButton: Button{text:'Apply', preferredSize: [50,25]},\
myButton2: Button{text:'Refresh Selection', preferredSize: [120,25]},\
myButton3: Button{text:'Help', preferredSize: [45,25]},\
},\
}");
// initially: no userSelection, ==> average
uiPal.grp.groupOne.myPanelThree.myDropDownList.selection = 0;
uiPal.grp.groupOne.addEventListener("click", onGroupOneClickEvent);
uiPal.grp.groupOne.myPanelThree.myDropDownList.onChange = onSelectionChange;
uiPal.grp.groupTwo.myButton.onClick = align;
uiPal.grp.groupTwo.myButton2.onClick = refresh;
uiPal.grp.groupTwo.myButton3.onClick = function showHelp(){helpPal.visible ? helpPal.hide() : helpPal.show();};
// update the userSelection straight away:
refresh();
// show the palette
helpPal.onResizing = uiPal.onResizing = function(){this.layout.resize();};
if (uiPal instanceof Window){
// show the palette
uiPal.center();
uiPal.show();
}
else{
uiPal.layout.layout(true);
};
//=================
// SCRIPT FUNCTIONS
//=================
// events
function onGroupOneClickEvent(ev){
var b = ev.target;
if (b instanceof RadioButton){
if (b.parent.text === "AXIS") state.dimension = b.tag
else state.axisDir = b.tag;
}
else if (b instanceof Image){
// ?
};
return;
};
function onSelectionChange(){
var n = this.selection.index;
state.refIndex = n;
if (n<this.items.length-1 && !Object.isValid(state.userSelection
)) alert("The reference layer is no longer valid. Refresh !"); return;
};
// other functions
function refresh(){
var
comp = app.project.activeItem,
dd = uiPal.grp.groupOne.myPanelThree.myDropDownList,
oldIndex = dd.selection.index,
oldText = dd.selection.text,
doAverage = oldIndex===dd.items.legnth-1,
n, N;
if (!(comp instanceof CompItem)) return; // ignore, or display some alert...
state.userSelection = comp.selectedLayers.slice(0).sort(function(a,b){return a.index-b.index;});
N = state.userSelection.length;
// update the dropdownmenu accordingly
// remove items
while (dd.children.length) dd.remove(dd.items[0]);
// add new
for (n=0; n<N; n++) dd.add("item", state.userSelection
.name); if (N>0) dd.add("separator");
dd.add("item", "Average");
// restore selection (default = average)
if (doAverage) dd.selection = dd.items.length-1
else{
for (n=0; n<N; n++) if (dd.items
.text === oldText) {dd.selection = n; break;}; if (n===N) dd.selection = dd.items.length-1;
};
return;
};
function checkValidity(){
// subroutine for "align"
// see if the script can align layers
var userSelection = state.userSelection, n, layer;
// no layers
if (userSelection.length<1) return false;
// invalid layers
for (n=0; n<userSelection.length; n++) if (!Object.isValid(userSelection
)){ alert("Some layers are no longer valid, press refresh !");
return false;
};
// non 3D layers
if (state.dimension === 2){
for (n=0; n<userSelection.length; n++){
layer = userSelection
; if (layer.threeDLayer || layer instanceof CameraLayer || (layer instanceof LightLayer && layer.lightType !== LightType.AMBIENT)){}
else{
alert( layer.lightType === LightType.AMBIENT ? "Can't align ambient lights" : "To align in the Z-direction, all layers should be 3D. Can't continue.");
return false;
};
};
};
// targetting a comp that is not active:
if (comp != app.project.activeItem) return confirm("The script layers belong to a comp that is not active. Continue ?");
return true;
};
function align(){
// unfold the state object
var
dimension = state.dimension,
axisDir = state.axisDir,
userSelection = state.userSelection,
refIndex = state.refIndex,
doAverage = refIndex>=userSelection.length,
refLayer;
// CHECK VALIDITY: cases of premature exit
if (!checkValidity()) return;
// THE SCRIPT CAN RUN :
app.beginUndoGroup("bt_3d_Align");
// align code there
if (doAverage){
// more code
}
else{
refLayer = userSelection[refIndex];
// more code
}
app.endUndoGroup();
return;
};
//
return;
})(this);
Copy link to clipboard
Copied
Thanks Xavier.Useful learning tool for what to do different the next time around. I appreciate it.
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more