Jump to: navigation, search

Difference between revisions of "Orion/Dialog Support"

(Dialogs (modal and modeless))
Line 114: Line 114:
  
 
* Destroying DOM nodes.  As of this writing, the DOM nodes are destroyed after the "hiding" sequence.  We assume clients recreate the dialog each time it is used.  If this usage pattern changes, we could always add an option that determines whether the dialog is actually destroyed when hidden
 
* Destroying DOM nodes.  As of this writing, the DOM nodes are destroyed after the "hiding" sequence.  We assume clients recreate the dialog each time it is used.  If this usage pattern changes, we could always add an option that determines whether the dialog is actually destroyed when hidden
 
  
 
=== Launching a dialog ===
 
=== Launching a dialog ===
Line 135: Line 134:
 
== Popup dialogs ==
 
== Popup dialogs ==
  
Popup dialogs provide lightweight, tooltip-style dialogs, including automatic dismissal by clicking outside of the dialog.
+
Popup dialogs provide lightweight, tooltip-style dialogs.  The dialog is not modal, does not have standard buttons, and is automatically dismissed if the user clicks outside of the dialog.  A popup's lifecycle is simpler than a regular dialog.  It is assumed that any information to be collected by the dialog should be triggered by an interaction inside the dialog.
  
 
=== Programming a popup ===
 
=== Programming a popup ===
 +
The concepts are similar to regular dialogs.  API definition is up to the client and the life-cycle is driven by the cleint.  The <tt>PopupDialog</tt> prototype is used for popups.
 +
 +
<pre>
 +
/**
 +
* Usage: <code>new OperationsDialog(options).show();</code>
 +
*
 +
* @name orion.webui.dialogs.OperationsDialog
 +
* @class A dialog that shows running operations.
 +
* @param {DOMNode} [options.triggerNode] The node that triggers the dialog.
 +
*/
 +
function OperationsDialog(options) {
 +
this._init(options);
 +
}
 +
 +
OperationsDialog.prototype = new popupdialog.PopupDialog();
 +
 +
OperationsDialog.prototype._init = function(options) {
 +
this._myOperations = [];
 +
this._initialize(options.triggerNode);
 +
};
 +
 +
OperationsDialog.prototype._bindToDom = function(parent) {
 +
this.$allOperationsLink.href = require.toUrl("operations/list.html"); //$NON-NLS-0$
 +
this._setOperationsVisibility();
 +
};
 +
</pre>
 +
 +
Templates are defined in a <tt>TEMPLATE</tt> variable as before, and DOM elements with id's are bound to $ variables in the popup.  The main difference in popups is that there is no frame or button container.  The client may still refer to the <tt>this.$parent</tt> variable to do DOM queries on the popup's content nodes.
 +
 
=== Launching a popup ===
 
=== Launching a popup ===
=== Examples ===
+
Like regular dialogs, dialogs are opened using the <tt>show()</tt> function.

Revision as of 15:40, 2 January 2013

In Orion 2.0, we no longer rely on the dijit toolkit for our dialog implementations. Instead, we use lightweight Orion constructs and CSS. This document describes how to implement an Orion dialog.

Dialogs (modal and modeless)

The module "orion/webui/dialog" provides support for dialog behavior. This class defines a lightweight life-cycle for dialogs, provides optional modal behavior, template support, and automatic button creation.

Programming a dialog

Dialogs are implemented as an object which uses the Dialog prototype. By convention, most Orion dialogs define a constructor whose sole parameter is an options object describing how the dialog is to be used. This will be shown in the example code, but keep in mind that the client-facing API of the dialog is completely up to the developer. The main requirement for implementation is that the Dialog prototype be used and the life-cycle functions called appropriately. An example is the best way to demonstrate the behavior.

define(['i18n!orion/widgets/nls/messages','orion/webui/dialog'], 
		function(messages, dialog) {

/* This is the client facing API of OpenResourceDialog.  Up to the developer to decide what this looks like. */

/**
 * Usage: <code>new OpenResourceDialog(options).show();</code>
 * 
 * @name orion.webui.dialogs.OpenResourceDialog
 * @class A dialog that searches for files by name or wildcard.
 * @param {String} [options.title] Text to display in the dialog's titlebar.
 * @param {orion.searchClient.Searcher} options.searcher The searcher to use for displaying results.
 * @param {Function} options.onHide a function to call when the dialog is hidden.  Optional.
 */

function OpenResourceDialog(options) {
	this._init(options);
}
	
/* Use the Dialog prototype to inherit the common dialog behavior.  */
OpenResourceDialog.prototype = new dialog.Dialog();

The Dialog life-cycle

The dialog life-cycle is driven by the client code. The Dialog prototype contains life-cycle functions that must be called by the client to initiate the dialog. The stages of a dialog's life and sample code are as follows:

  • Setting up the dialog template. The dialog's content is described in an HTML string specified in a dialog variable called TEMPLATE. By convention, most Orion dialogs define this variable in-line in the .js file that defines the dialog. Developers who prefer to use separate HTML files may do so using any preferred template/fragment mechanism. The key point is that the HTML is bound to the variable before initiating the dialog creation.

The OpenResourceDialog defines the TEMPLATE in line.

OpenResourceDialog.prototype.TEMPLATE = 
'<div role="search">' + //$NON-NLS-0$
  '<div><label for="fileName">'+messages['Type the name of a file to open (? = any character, * = any string):']+'</label></div>' + //$NON-NLS-1$ //$NON-NLS-0$
  '<div><input id="fileName" type="text" placeholder="'+messages['Search']+'"</input></div>' + //$NON-NLS-1$ //$NON-NLS-0$
  '<div id="crawlingProgress"></div>' + //$NON-NLS-0$
  '<div id="favresults" style="max-height:400px; height:auto; overflow-y:auto;"></div>' + //$NON-NLS-0$
  '<div id="results" style="max-height:400px; height:auto; overflow-y:auto;" aria-live="off"></div>' + //$NON-NLS-0$
  '<div id="statusbar"></div>' + //$NON-NLS-0$
'</div>'; //$NON-NLS-0$
  • Dialog definition and setting up the basic (non-DOM) options. This is driven by the client API. Our convention is to call an internal _init function which reads the client options and sets up any necessary internal state. The last thing to do in this function is to call the Dialog._initialize function. The initialize sequence will look for special variables that drive the behavior of a dialog. For example, a title will cause a title string to appear in the dialog frame. An array of buttons will cause buttons to be created. In this example, we are just setting a title. The rest of the work in here pertains to our specific implementation until we call _initialize.
OpenResourceDialog.prototype._init = function(options) {
	// Set values for things that dialog cares about, such as title.  
	this.title = options.title || messages['Find File Named'];

	// Set internal values, validate the options, etc.
	this._searcher = options.searcher;
	this._onHide = options.onHide;
	this._contentTypeService = new mContentTypes.ContentTypeService(this._searcher.registry);
	if (!this._searcher) {
		throw new Error("Missing required argument: searcher"); //$NON-NLS-0$
	}	
	...

	// If the dialog template is not in the TEMPLATE variable by default, then read the template from a file and set into the variable now.

	// Start the dialog initialization.
	this._initialize();
};
  • Binding to the DOM elements. Hooking events and other behavior that is dependent on the existence of the DOM elements happens in the _bindToDOM function. This function is called by the dialog during the initialization process. When this function is called, you can use the following variables to access the DOM:
    • this.$frameParent refers to the parent node of the entire dialog. This is usually the document body, and typically does not need to be accessed by application dialog code.
    • this.$frame refers to the div element that frames the dialog. This typically does not need to be accessed by application dialogs.
    • this.$parent refers to the div element that parents the client template. Clients may want to refer to this parent when querying the dialog content for a particular node.
    • this.$buttonContainer refers to the div element containing any autogenerated buttons. This won't exist if no buttons were defined.
    • Your content field id's are bound to $ instance variables during the _initialize function. For example:
this.$fileName.addEventListener("input", function() { // do something }, false);

The variable $fileName refers to the DOM node whose id is "fileName". All nodes assigned an id in the template will be bound to a $ variable.

  • Modal behavior. If the dialog should have modal behavior, set a modal flag before initialization.
MyModalDialog.prototype._init = function(options) {
	// Set values for things that dialog cares about, such as title.  
	this.title = "My Modal Dialog";
	this.modal = true;
        ...
  • Showing the dialog. The dialog will not appear until the show() function is called. This function is implemented by the Dialog prototype. If your dialog needs to do some extra work during this time, the following optional functions may be implemented:
    • _beforeShowing is called just before the dialog is made visible. Here, dom node values may be populated, etc.
    • _afterShowing is called just after the dialog is made visible. This is a good place to set initial focus in the dialog.
  • Finishing the dialog. Most dialogs provide a button that the user pushes to indicate that the dialog is completed. The text for the button is specified, in addition to the callback to use when the button is pressed. In this example, an "OK" button is created, and it will call the application's done function.
MyModalDialog.prototype._init = function(options) {
	// Set values for things that dialog cares about, such as title.  
	this.title = "My Modal Dialog";
	this.modal = true;
        ...
	this.buttons = [{text: messages['OK'], callback: this.done.bind(this)}]; 
        this._initialize();
}
  • Hiding and cleaning up. The standard dialog frame provides a close icon that can be used to hide the dialog. The application dialog will also typically hide the dialog when it's finished. Either way, the hide() function is used to hide the dialog. To clean up any listeners or other resources allocated by the dialog, use the following functions:
    • _beforeHiding is called just before the dialog is hidden. Typically listeners are removed here.
    • _afterHiding is called just after the dialog is hidden for any final cleanup. The DOM nodes still exist during this call.
  • Destroying DOM nodes. As of this writing, the DOM nodes are destroyed after the "hiding" sequence. We assume clients recreate the dialog each time it is used. If this usage pattern changes, we could always add an option that determines whether the dialog is actually destroyed when hidden

Launching a dialog

As mentioned previously, the developer of an individual dialog determines the API, but using the conventions discussed above, a typical sequence is to create the dialog using a constructor, passing in some function that is called when the dialog is finished. Then, the client calls the show() function to show the dialog. Most of the nitty gritty work done by the caller is done in the function passed to the dialog on creation.

var dialog = new openResource.OpenResourceDialog({
	searcher: searcher, 
	searchRenderer:searcher.defaultRenderer, 
	favoriteService:favoriteService,
	onHide:  function() { 
		if (editor) {
			editor.getTextView().focus(); 
		}
	}
});
dialog.show();

Popup dialogs

Popup dialogs provide lightweight, tooltip-style dialogs. The dialog is not modal, does not have standard buttons, and is automatically dismissed if the user clicks outside of the dialog. A popup's lifecycle is simpler than a regular dialog. It is assumed that any information to be collected by the dialog should be triggered by an interaction inside the dialog.

Programming a popup

The concepts are similar to regular dialogs. API definition is up to the client and the life-cycle is driven by the cleint. The PopupDialog prototype is used for popups.

/**
 * Usage: <code>new OperationsDialog(options).show();</code>
 * 
 * @name orion.webui.dialogs.OperationsDialog
 * @class A dialog that shows running operations.
 * @param {DOMNode} [options.triggerNode] The node that triggers the dialog.
 */
function OperationsDialog(options) {
	this._init(options);
}
	
OperationsDialog.prototype = new popupdialog.PopupDialog();

OperationsDialog.prototype._init = function(options) {
	this._myOperations = [];
	this._initialize(options.triggerNode);
};
	
OperationsDialog.prototype._bindToDom = function(parent) {
	this.$allOperationsLink.href = require.toUrl("operations/list.html"); //$NON-NLS-0$
	this._setOperationsVisibility();
};

Templates are defined in a TEMPLATE variable as before, and DOM elements with id's are bound to $ variables in the popup. The main difference in popups is that there is no frame or button container. The client may still refer to the this.$parent variable to do DOM queries on the popup's content nodes.

Launching a popup

Like regular dialogs, dialogs are opened using the show() function.