Sunday, November 20, 2011

Linking to the power of Orion

In my last post, I mentioned our Orion command framework, which allows our pages to define the commands that a user can run on a particular page. We were evolving the command definition to include the specification of what parameters it requires.  While I focused more on the UI in the last discussion, now I'd like to talk about how this concept will allow you to link to Orion in interesting ways.

Let's follow a simple example.  One of the things a user can do from the Orion Git Repositories page is clone a git repository into an Orion folder. In the first iteration of the command framework, any work done after the user clicked the "Clone Repository" link was done in the command's callback.

cloneGitRepositoryCommand = new mCommands.Command({
 name : "Clone Repository",
 tooltip : "Clone an existing Git repository to a folder",
 id : "eclipse.cloneGitRepository",
 callback : function(item, commandId, domId, userData) {
  var dialog = new orion.git.widgets.CloneGitRepositoryDialog({
   serviceRegistry: serviceRegistry,
   fileClient: fileClient,
   func: function(gitUrl, path, name) {
     // do the real work

But now we allow the command to specify that it requires a URL parameter. We also indicate that there are additional parameters that can be collected if the user wants to specify additional options. (These snippets are intended to show you how things work, but this interface will certainly change before the next release milestone.)

var cloneParameters = {};
cloneParameters.url = {label: 'Repository URL:', type: 'url'}; 
cloneParameters.options = true;  // indicate that we could collect additional options 

cloneGitRepositoryCommand = new mCommands.Command({
 name : "Clone Repository",
 tooltip : "Clone an existing Git repository to a folder",
 id : "eclipse.cloneGitRepository",
 parameters: cloneParameters,
 callback : function(item, commandId, domId, userData, parameters) {
  if (parameters && parameters.url.value) {
   // do the real work
  } else {
   // we could trigger a dialog to get the parameters, or report an error, etc...

So far, we've just defined a command. Nothing is going to show up in the UI until we register a command contribution on a page. In the contribution, we can specify the DOM node where the command should be shown, such as a toolbar, and a key binding that can be used to trigger the same command via the keyboard. But the fun really starts now that we allow a URLBinding to be associated with a command. With a URL binding, a page can define a URL (fragment) query parameter that can be used to trigger command, and the parameter name of any value provided in the URL. For the clone command, the URL binding looks like this.

var urlBinding = new mCommands.URLBinding("cloneGitRepository", "url"));
commandService.registerCommandContribution("eclipse.cloneGitRepository", ..., urlBinding);

This means that the query parameter cloneGitRepository should trigger the command, and the value in the URL should be bound to the url parameter in the command. From Orion's point of view, opening the Git Repositories page with the following link calls the same code as if the user had clicked on the command in the UI and typed in the URL:

One important note. A command invoked by a URL binding is not called by this URL. Rather the page is opened in a state for collecting the parameters. We just prefill the parameter information, and the user can hit the Enter key to start the clone, or click the More button to provide additional information. Our URL needs to be a stable GET. Reloading the URL multiple times should never alter the state of the resource on the page. So user intervention is always required in order to actually invoke the command. The URL binding, much like a key binding, simply gives the user a shortcut for starting the process.

We think the URL binding is a powerful concept. It allows us to start exposing linkable bits of function in a way that is completely separate from the code that defines the function. Being able to specify parameters in the URL syntax means that some other site can use its own logic to figure out what the user wants, and then link to an Orion page, providing contextual information.

Our favorite simple example is a bugzilla page that could use the product and component information in the bug definition to figure out which git repository the user might want to clone to look at the code. Another idea is the ability to author "cue card" style help systems that could guide the user through some set of tasks in Orion, simply by linking to the various commands involved in the steps.

We can't wait to see what you might do with this idea.

Monday, November 7, 2011

Collecting Bits of Input

I've been looking around lately at the way different sites collect input when performing a task, and how we can do better in Orion.

For the time being, I'm ignoring the main input task in Orion - editing source code. And I'm not talking about a form-based UI where describing or creating something is the main task at hand.  Often, we just need a little bit of info (a name, a URL, a password) in order to do something. In Orion, we have a grab bag of techniques for collecting this kind of input.
  • Some times, we just put an input field near the place you are working.
  •  When we need to know more than one thing, or want to provide additional options, we open a dialog that collects parameters.
  • In some cases (where we were doing something quick and dirty), we rely on the browser's prompt dialogs.
These subtle differences can burden a user over time. We have learned in our years working on Eclipse that the best way to ensure some consistency is to provide code that does the right thing. Reducing the burden on the developer helps the user, too.

We have a command framework in Orion that lets pages define commands that the user can run on a particular page. This allows us to separate the presentation decisions from the work performed by the command. For example, if you want to define a command that should be available on files in the Orion navigator, you can describe the command and provide code that does the work, and we are free to play around with where and how the command appears. Is it a button or a link? Is it text, icon, or both? Does it appear on hover or is it always there? Where on the page does it appear (in-line, in a menu or toolbar, in the banner?) The presentation decisions can evolve without a lot of hassle for developers who contribute these commands. (I'll write another post soon about how we see our page layouts evolving in this area.)

This all sounds good, but today, we currently rely on the handler, the code that performs the command, to gather parameters. For example, when you press a "New File" link, the handler code is the one prompting you for the name and creating the file. This has its share of problems, though, and some of our new release goals for Orion 0.4 add more excitement:
  • As we've seen, it means that different commands collect parameters in different ways, causing a little extra thinking by the user.
  • We're finding that dialog placement and interaction can be problematic on tablets, and even more problematic when an on-screen keyboard pops up to collect input.
  • We want to explore scenarios where a link from some other site leads you to an Orion page that requires input. So the way we gather input needs to be even more visible and predictable when you arrive at a page that's already asking for something, without being overly disruptive when you are already working on the page and need to answer that same question.
  • We need to make sure the solution is accessible. So using a common implementation can help us focus our efforts here (pun intended).
If we can instead allow a command to describe the parameters it needs to collect, we can then provide a consistent, appropriate presentation for gathering that input. Perhaps we'll come up with a way that plays well in all the scenarios we want to support. We might instead learn we need different presentations for these different situations. From the command handler's point of view, though, the implementation won't get called until the necessary input has been gathered.

I'm currently working on a first stab at this idea. From a presentation point of view, I started out with some ideas that I'd like to validate:
  • The "form" or "dialog" that collects input should be inserted in the DOM vs. overlayed as a modal dialog or slideout. I think this can help with zooming and scrolling issues on tablets.
  • There should be enough animation that a user arriving at a page for the first time notices right away where the input is needed. But the animation should be fast and well supported on different devices, so I'm using CSS3 transitions rather than a JS library.
  • Parameter types should be described using HTML5 input types. This means the browser can optimize the input control, and for some devices, it can mean customized keyboards (numeric, URL) that make input faster.
  • For added fun, my first implementation adds the speech attribute to the input field so that parameters can be spoken. If we can avoid popping up a keyboard altogether, that might make providing simple input much faster. For complex forms and dialogs, it might be a silly idea, but it makes for a fun demo right now.
My first try uses a slide-in parameter collector. I haven't spent much time on cosmetics, so expect it to look snazzier once our graphic designer gets a look at it. In the three cases shown above, the input is now collected by the command framework, in the same way (location depends on where the command appears.)
Go to Line parameter collector (with spinner courtesy of "number" input type)
New File parameter collector (speech enabled)
Git Clone parameter collector
I'm not done with the code yet, but I hope to release it this week and get more user feedback on how it plays. In the meantime, I'm wondering if you've seen a technique somewhere that you like, that you think would meet similar goals?