Sunday, 3 March 2013

Trees in Google Apps Script UI


Trees in Google Apps Script

The tree widget in the Apps Script user interface presented me with a few issues. I persevered to get a solution that worked for me.

What did I want?

  • Tree element can be selected
  • Selection reflected by a change of style attributes (eg bold, red etc)
  • Selection of one element turns selection of previous element off
  • Take an action based on the selection made (a handler)

Tree Widget

Apps Script UI does provide a Tree widget and Google helpfully tells you it is based on GWT. I suspect that if you are an expert in GWT and its documentation you would not be using Apps Script.
Initially, I just set the tree branches to text with no widget (code below). This provided a standard indication that I had clicked (selected) the tree branch - a blue highlight. Unfortunately the selection handler that is fired, currently only tells you that the tree has been selected and not which branch or leaf. There is a long outstanding issue for this. EDIT 25/4/13. Now Fixed
function doGet() {
 var start = new Date();
 var app = UiApp.createApplication();
 var target = app.createLabel().setId("target");  
 var panel = app.createAbsolutePanel();
 // create tree
 var selhandler = app.createServerHandler("selector").addCallbackElement(panel);
 var tree = app.createTree().setAnimationEnabled(false).setId('FullTree').setTag("").addSelectionHandler(selhandler);
 var handler = app.createServerHandler("selector").addCallbackElement(tree);
 // build a tree
 for (i=0;i<100;i++){
   var str = "label"+(i+1);
   var str1 = "item"+(i+1);
   //  create level 1 branch
   var item1 = app.createTreeItem(”TEXT”).setId(str1).setState(true,true);
   // add level 2 branches to level 1
   addsub(app,item1,tree);
   // add level 1 branch to tree
   tree.addItem(item1);
 }
 panel.add(tree);
 panel.add(target);
 app.add(panel);
 Logger.log("Elapsed:"+(new Date() - start));
 return app;
 
}

Putting a label widget in the branches instead of the plain text allowed a click handler to be used. In the handler, parameter.source provides an indicator of the item clicked. But branch is no longer highlighted when selected so the user does not get feedback of what is selected.
At this point, I thought of setting the styling for selected/ not-selected myself. What was I thinking! You can use a client handler on click to set the attributes of the label widget when you click it. Unfortunately, client handlers do things to the source widget or statically named target widgets so you can’t remember the currently selected item and set its styling in a toggle action.
Back to the server handler then. Yes, if you have remembered the previous state, you can use the server handler to set the style accordingly. BUT, the only way that you can know the state in the server handler is to pass it via the parameters. This led me to the final problem, which was that the users would be able to click more than one widget faster than the server handler can deal with it. Multiple parallel server handlers are fired and the “previous state” is dependent on timing … instead of a toggle operation, the user interface can get multiple widgets marked as selected. The only way around this is to restyle all the widgets to the unselected state and then style the selected widget. To say this makes for an unresponsive interface is an understatement.

Solution

We need a widget that has built in “selected” behaviour and to put that in the tree. So use a radio button for each of the tree elements.
  • Highlighted (and filled button) when selected - fast, in browser, change
  • Exclusive selection built-in
  • Fires click handler for server action
  • Developer can change base style
  var label1 = app.createRadioButton("XOX",str).setId(str).setName("SameGroup").addClickHandler(handler).setTag(str).
   setStyleAttributes({'fontWeight':'bold','fontSize':'14pt', "color" : "black","fontFamily":"arial,sans-serif"});
Note that the name of each radio button has to be the same to put them into a common group for the toggle action to work.

No comments: