10 Jan 2018
Customizing ModelStructurePanel behavior in Forge Viewer
The tips of the week today is about customizing the behavior of the ModelStructurePanel without adding too much client code. It is inspired from the answer I posted on Stackoverflow to address that question: Prevent zoom in Forge viewer when clicking in Model Browser.
The ModelStructurePanel was rewritten in Viewer version 3.2.1 and its behaviour has slightly changed as mentioned in the thread above. So what if you want to quickly customize the actions that occur when user is clicking a component in the hierarchy?
In order to know what's possible, we need to take a closer look at the implementation of Autodesk.Viewing.Extensions.ViewerModelStructurePanel in the source of the viewer:
First of all the onClick method: we can see that this method checks for key modifiers [Ctrl, Shift, Alt] and appends it to the event, so you can handle clicks differently if one of those keys is pressed at the time of the click.
ViewerModelStructurePanel.prototype.onClick = function (node, event) {
if (this.isMac && event.ctrlKey) {
return;
}
var that = this;
var key = "click";
if (that.ctrlDown(event)) {
key += "Ctrl";
}
if (event.shiftKey) {
key += "Shift";
}
if (event.altKey) {
key += "Alt";
}
if (this.clickConfig && this.clickConfig[key]) {
that.handleAction(this.clickConfig[key]["onObject"], node);
} else {
this.viewer.select(node);
}
};
This method is calling handleAction if an entry is specified in the clickConfig member, which is initialized from the options passed to the panel constructor as follow:
var kDefaultDocStructureConfig = {
"click": {
"onObject": ["toggleLeavesSelection"]
},
"clickShift": {
"onObject": ["toggleMultipleLeavesSelection"]
},
"clickCtrl": {
"onObject": ["toggleMultipleLeavesSelection"]
}
};
function ViewerModelStructurePanel(viewer, title, options) {
// ... skipping stuff for clarity ...
this.clickConfig = (options && options.docStructureConfig)
? options.docStructureConfig
: kDefaultDocStructureConfig;
And finally the implementation of handleAction, which shows us the available default actions:
ViewerModelStructurePanel.prototype.handleAction = function (actionArray, dbId) {
for (var action in actionArray) {
switch (actionArray[action]) {
case "toggleLeavesSelection":
toggleLeavesSelection(this, dbId);
break;
case "toggleMultipleLeavesSelection":
toggleMultipleLeavesSelection(this, dbId);
break;
case "selectOnly":
this.ignoreNextSelectionChange = true;
this.viewer.select(dbId);
break;
case "deselectAll":
this.ignoreNextSelectionChange = true;
this.viewer.select([]);
break;
case "selectToggle":
this.ignoreNextSelectionChange = true;
this.viewer.toggleSelect(dbId);
break;
case "isolate":
this.viewer.isolate(dbId);
break;
case "showAll":
this.viewer.isolate(null);
break;
case "focus":
this.viewer.fitToView();
break;
case "hide":
this.viewer.hide(dbId);
break;
case "show":
this.viewer.show(dbId);
break;
case "toggleVisibility":
this.viewer.toggleVisibility(dbId);
break;
}
}
};
Based on those info, you can easily replace the default ViewerModelStructurePanel by a new one and take the opportunity to provide your own options in the constructor, a safe place to perform the replacement is to use the Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT as we know the model structure is fully loaded at this time:
viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, function() {
var options = {
docStructureConfig: {
click: {
onObject: ["selectOnly"] //instead of toggleOverlayedSelection
},
clickShift: {
onObject: ["isolate"] //instead of toggleMultipleOverlayedSelection
},
clickCtrl: {
onObject: ["selectToggle"] //instead of toggleMultipleOverlayedSelection
}
}
}
var customModelStructurePanel =
new Autodesk.Viewing.Extensions.ViewerModelStructurePanel(
viewer, 'Browser', options)
viewer.setModelStructurePanel(customModelStructurePanel)
})
And that's it for today! For a more powerful customization you can of course inherit your own class from ViewerModelStructurePanel and reimplement the body of handleAction method. For a starting point you can refer to my previous article: Supporting multiple models in the new ModelStructurePanel