As outlined in https://www.daz3d.com/forums/discussion/140571/order-objects-in-scene-tab-drag-drop, I would love to have a way to change the order of items in your scene view/structure tab.
I'm a visually oriented guy, and I like to have things that are close in meaning visually close as well.
So I bit the bullet and looked at how scripts in DAZ work.
Surprisingly, it's a rather easy JS based simply API system that doesn't require a ton of getting used to. (Except for the GUI part - they're quite ugly to set up IMO (Although I haven't spent much time with it yet)) - and I wrote a script that lets you move nodes around, AS LONG AS THEY HAVE PARENTS.
Because the API exposes methods to insert nodes into a specific index of other nodes, I can move items around when there is a parent to "move" the item into.
When you've selected an item that does not have a parent, you should be presented with the option to move all scene contents in a group which I simply call "Scene Root".
- Usage should be pretty easy: Move the selected item up and down past its siblings under a parent by clicking "up" and "down"
- Move the selected item out of its parent node by clicking "Unparent"
- Make the upper sibling the parent of the currently selected node (i.e. move selected node into the one above it)
And navigate the selection via the corresponding buttons below.
Video:
![image]()
// V0.1
var selSize = 0;
var hasParent = false;
var numChildren = 0;
var isFirst = false;
var isLast = false;
var fos = Scene.getSelectedNode(0);
var los = Scene.getSelectedNode(selSize-1);
var par = fos.getNodeParent();
var startSelect = null;
var statAbsPos;
var statAbsRot;
var statAbsScale;
function moveInView(a){
var selnums = Scene.getNumSelectedNodes();
for(var i = 0; i<selnums; i++){
var selnode = Scene.getSelectedNode(i);
if(selnode.getNodeParent()){
var parnode = selnode.getNodeParent();
var cind = parnode.findChildIndex(selnode);
// msg(selnode.getLabel() + " = " + cind + " + " + a + " /" + parnode.getNumNodeChildren());
if(cind + a>=0 && cind + a < parnode.getNumNodeChildren()){
parnode.moveNodeChildToIndex(selnode, cind + a, true);
}
}
else{
if(ask("No parent node available.\nShould all scene Nodes be moved into a Root Node (Group)?", "No Parent", "Cancel", "OK")){
rootAll();
moveInView(a);
}
}
}
updateButtons();
}
function unparent(){
var selnums = Scene.getNumSelectedNodes();
for(var i = 0; i<selnums; i++){
var selnode = Scene.getSelectedNode(i);
var parentnode = selnode.getNodeParent();
if(parentnode){
if(selnode.getNodeParent().getNodeParent()){
saveTrans(selnode);
selnode.getNodeParent().getNodeParent().addNodeChild(selnode);
restoreTrans(selnode);
}
else{
parentnode.removeNodeChild(selnode, true);
}
}
}
updateButtons();
}
function doparent(){
var selnums = Scene.getNumSelectedNodes();
for(var i = 0; i<selnums; i++){
var selnode = Scene.getSelectedNode(i);
var parentnode = selnode.getNodeParent();
if(parentnode){
var selfid = parentnode.findChildIndex(selnode);
var newpar = parentnode.getNodeChild(selfid-1);
newpar.addNodeChild(selnode, true);
}
}
updateButtons();
}
function selectSibling(i){
getStates();
if(selSize<=0){
Scene.getNodeChild(0).selected = true;
return;
}
var sib = getSibling(fos,i);
sib.select(true);
fos.select(false);
updateButtons();
}
function selectParent(){
getStates();
if(hasParent && fos.getNodeParent().getNodeParent()){
selectNone();
fos.getNodeParent().select(true);
}
updateButtons();
}
function selectChild(){
getStates();
if(numChildren>0){
selectNone();
fos.getNodeChild(0).select(true);
}
updateButtons();
}
var wDlg = new DzDialog;
wDlg.caption = "Re-Order Scene Object Tree View";
var wDlgLayout = new DzGridLayout( wDlg );
wDlgLayout.margin = 5;
wDlgLayout.spacing = 5;
var wBtnGrp = new DzGroupBox( wDlg );
wBtnGrp.title = "Move in List";
wBtnGrp.columns = 3;
var bugrid = new DzGridLayout(wBtnGrp);
bugrid.margin = 10;
bugrid.spacing = 4;
var wMoveUp = new DzPushButton(wBtnGrp);
wMoveUp.text = "Up";
wMoveUp.minWidth = 80;
wMoveUp.maxHeight = 20;
wMoveUp.clicked.connect(function(){moveInView(-1)});
bugrid.addWidget(wMoveUp, 0, 1);
var wUnparent = new DzPushButton(wBtnGrp);
wUnparent.text = "Unparent";
wUnparent.minWidth = 80;
wUnparent.maxHeight = 20;
wUnparent.clicked.connect(unparent);
bugrid.addWidget(wUnparent, 1, 0);
var wParent = new DzPushButton(wBtnGrp);
wParent.text = "Parent";
wParent.minWidth = 80;
wParent.maxHeight = 20;
wParent.clicked.connect(doparent);
bugrid.addWidget(wParent, 1, 3);
var wMoveDown = new DzPushButton(wBtnGrp);
wMoveDown.text = "Down";
wMoveDown.minWidth = 80;
wMoveDown.maxHeight = 20;
wMoveDown.clicked.connect(function(){moveInView(1)});
bugrid.addWidget(wMoveDown, 3, 1);
/////////////// Navigation
var wNavGrp = new DzGroupBox( wDlg );
wNavGrp.title = "Navigate Selection in List";
wNavGrp.columns = 3;
var nagrid = new DzGridLayout(wNavGrp);
nagrid.margin = 10;
nagrid.spacing = 4;
var wnMoveUp = new DzPushButton(wNavGrp);
wnMoveUp.text = "Up";
wnMoveUp.minWidth = 80;
wnMoveUp.maxHeight = 20;
wnMoveUp.clicked.connect(function(){selectSibling(-1)});
nagrid.addWidget(wnMoveUp, 0, 1);
var wnUnparent = new DzPushButton(wNavGrp);
wnUnparent.text = "Up Parent";
wnUnparent.minWidth = 80;
wnUnparent.maxHeight = 20;
wnUnparent.clicked.connect(selectParent);
nagrid.addWidget(wnUnparent, 1, 0);
var wnParent = new DzPushButton(wNavGrp);
wnParent.text = "Into Children";
wnParent.minWidth = 80;
wnParent.maxHeight = 20;
wnParent.clicked.connect(selectChild);
nagrid.addWidget(wnParent, 1, 3);
var wnMoveDown = new DzPushButton(wNavGrp);
wnMoveDown.text = "Down";
wnMoveDown.minWidth = 80;
wnMoveDown.maxHeight = 20;
wnMoveDown.clicked.connect(function(){selectSibling(1)});
nagrid.addWidget(wnMoveDown, 3, 1);
wDlgLayout.addMultiCellWidget( wBtnGrp, 0, 0, 0, 1 );
wDlgLayout.addMultiCellWidget( wNavGrp, 1, 1, 0, 1 );
var wDlgBtnsGB = new DzGroupBox( wDlg );
wDlgBtnsGB.flat = true;
var wDlgBtnsLyt = new DzGridLayout( wDlgBtnsGB );
wDlgBtnsLyt.margin = 5;
wDlgBtnsLyt.spacing = 5;
var wAcceptBtn = new DzPushButton( wDlgBtnsGB );
wAcceptBtn.text = "OK";
wAcceptBtn.minWidth = 80;
wAcceptBtn.maxHeight = 20;
wDlg.setAcceptButton( wAcceptBtn );
wDlgBtnsLyt.addWidget( wAcceptBtn, 1, 0 );
wDlgLayout.addMultiCellWidget( wDlgBtnsGB, 2, 2, 0, 1 );
wDlg.maxWidth = wDlg.minWidth;
wDlg.maxHeight = wDlg.minHeight;
updateButtons();
if(selSize<=0){
Scene.getNodeChild(0).select(true);
updateButtons();
}
if(!hasParent && ask("No parent node available.\nShould all scene Nodes be moved into a Root Node (Group)?", "No Parent", "Cancel", "OK")){
startSelect = Scene.getSelectedNode(0);
rootAll();
}
var rn = Scene.findNode("Scene Root");
if(!hasParent && rn && rn.getNumNodeChildren()>0){
selectNone();
if(startSelect!=null){
startSelect.select(true);
}
else{
rn.getNodeChild(0).select(true);
}
updateButtons();
}
wDlg.exec()
function msg(s,t,c1,c2,c3){
t = t==undefined?"Message":t;
c1 = c1==undefined?"OK":c1;
c2 = c2==undefined?"":c2;
c3 = c3==undefined?"":c3;
if(c2!=""){
if(c3!=""){
return MessageBox.information(s,t,c1,c2,c3);
}
return MessageBox.information(s,t,c1,c2);
}
return MessageBox.information(s,t,c1);
}
function warn(s,t,c1,c2,c3){
t = t==undefined?"Warning":t;
c1 = c1==undefined?"OK":c1;
c2 = c2==undefined?"Cancel":c2;
c3 = c3==undefined?"":c3;
if(c2!=""){
if(c3!=""){
return MessageBox.warning(s,t,c1,c2,c3);
}
return MessageBox.warning(s,t,c1,c2);
}
return MessageBox.warning(s,t,c1);
}
function ask(s,t,c1,c2,c3){
t = t==undefined?"Message":t;
c1 = c1==undefined?"No":c1;
c2 = c2==undefined?"Yes":c2;
c3 = c3==undefined?"":c3;
if(c2!=""){
if(c3!=""){
return MessageBox.question(s,t,c1,c2,c3);
}
return MessageBox.question(s,t,c1,c2);
}
return MessageBox.question(s,t,c1);
}
function rootAll(){
var baseNode = new DzGroupNode();
baseNode.name = "Scene Root";
baseNode.label = "Scene Root";
Scene.addNode(baseNode);
var allnodes = Scene.getNodeList();
var j = 0;
for(var i = 0; i<allnodes.length; i++){
if(allnodes[i].name == "Scene Root" || allnodes[i].getNodeParent())
continue;
baseNode.addNodeChild(allnodes[i], true);
baseNode.moveNodeChildToIndex(allnodes[i],j);
j++;
}
}
function getSibling(n, i){
var parnode = n.getNodeParent();
var cur = parnode.findChildIndex(n);
if(cur + i < 0){
return parnode;
}
if(cur + i >= parnode.getNumNodeChildren()){
return nextOrParent(n, i);
}
return parnode.getNodeChild(cur + i);
}
function nextOrParent(c, i){
var n = c;
var parnode = n.getNodeParent();
var cur = parnode.findChildIndex(n);
if(cur + i >= parnode.getNumNodeChildren()){
if(n.getNodeParent()){
return nextOrParent(n.getNodeParent(), i);
}
else{return null;}
}
return parnode.getNodeChild(cur + i);
}
function getStates(){
selSize = Scene.getNumSelectedNodes();
hasParent = false;
isFirst = true;
isLast = true;
fos = Scene.getSelectedNode(0);
los = Scene.getSelectedNode(selSize-1);
par = fos.getNodeParent();
numChildren = fos.getNumNodeChildren();
if(selSize>0 && par)
hasParent = true;
if(hasParent && par.findChildIndex(fos)>0)
isFirst = false;
if(hasParent && par.findChildIndex(los) < par.getNumNodeChildren()-1)
isLast = false;
// mag("index: " + par.findChildIndex(los) + "; num children: " + par.getNumNodeChildren());
}
function setButtons(){
wMoveUp.enabled = !isFirst;
wParent.enabled = !isFirst;
wMoveDown.enabled = !isLast;
wUnparent.enabled = hasParent;
wnMoveDown.enabled = !isLast;
wnMoveUp.enabled = hasParent;
wnParent.enabled = numChildren>0;
wnUnparent.enabled = hasParent && fos.getNodeParent().getNodeParent();
}
function updateButtons(){
getStates();
setButtons();
}
function selectNone(){
for(var i = 0; i<Scene.getNumSelectedNodes(); i++){
Scene.getSelectedNode(i).select(false);
}
}
function saveTrans(n){
statAbsPos = n.getWSPos();
statAbsRot = n.getWSRot();
statAbsScale = n.getWSScale();
}
function restoreTrans(n){
n.setWSPos(statAbsPos);
n.setWSRot(statAbsRot);
n.setWSScale(statAbsScale);
}
I hope this is useful to at least one other person :)
Make it available via the main menu by right clicking -> Create Custom Action.
![]()