The Early Beginnings
25 Feb 99
contact
Dialogs are alive and somewhat well in Mozilla, and this document attempts to explain their current status.
Change history
25 Feb 99 - More careful about case sensitivity in the examples. Updated example of creating a dialog from C++.
24 Feb 99 - More careful about namespaces in the examples. Added a note about XUL Document method getElementByID
23 Feb 99 - AppCoresManager name changed to XPAppCoresManager
18 Feb 99 - Updated to new command architecture and initialization code.
17 Feb 99 - Revised JavaScript for debriefing dialog elements at dismissal time. Added note about DOM interfaces available to XUL widgets (also in the Examples section).
16 Feb 99 - Revised JavaScript for initializing dialog elements at window load time
This document is not intended to be a comprehensive syntax or reference manual. At the moment it's an apology for the current state of things, and a design document. With care and time, it should grow into a useful starting place for making windows and writing dialogs. The author thinks that feedback on what's missing from the document would help build a better document, so the quality of this thing is in the reader's hands. Keep that in mind while you're cursing me.
In the meantime, this document also serves as a roadmap of XPFE's dialog story. Interested parties would please review it to determine whether the package we plan to deliver will meet their needs.
Current XPFE design goals state that dialogs get no special treatment; they're just windows like any other. Windows can have parent windows, and be displayed modally, and have control callbacks, and so behave like dialogs. But our goal is that the same application code that instantiates a browser window will serve equally to instantiate a dialog window. The difference lies in the window content, and a couple of parameters controlling modality and that sort of thing.
Currently, all window control mechanisms flow through JavaScript. That is, any capabilities a window may have besides taking up space on the desktop are specified in the XUL describing the window, and the binding between the window content and the application is done through JavaScript in the XUL. That binding can be very short: it can fall through to application C code very quickly, but JavaScript will be part of the process. (Note that we have not yet run into any threading problems with this model, but we kind of expect to.)
JavaScript is easily added to a window: just include it in the XUL window description and follow a short but peculiar dance to hook it up. Hooking up C code involves writing a C++/JavaScript interface, and calling it from JavaScript. That interface is an AppCore, and there's a separate and much more impressive document describing them and explaining how to build them.
So, there are two ways to make a window or dialog: directly through C++ and
from JavaScript. The resulting window is an instance of nsWebShellWindow.
This means it currently contains a hulking bunch of hackery for hooking up browser-window
specific contents. This will be cleaned up in the future.
Note there is currently no way to make a modal dialog or alert. We are limited to simply bringing up new XUL windows at this time. Also note that windows are not sized to content. These are merely somewhat glorified browser windows, to which automatically sizing themselves to match their content is something of an eyebrow-furrowing concept. Specifying the size of a dialog is another future enhancement.
The C++ interface is nsIAppShellService::CreateDialogWindow().
It's currently very clunky and will change (you'll see that statement many times
in this document). But at time of writing, you create a new window like this:
nsresult rv;
nsString controllerCID;
nsIAppShellService *appShell;
nsIURL *url;
nsIWidget *parent, *window;
nsIStreamObserver *observer;
nsIXULWindowCallbacks *callbacks;
PRInt32 width, height;
window = nsnull;
rv = nsServiceManager::GetService(kAppShellServiceCID,
kIAppShellServiceIID,
(nsISupports**) &appShell);
if (NS_SUCCEEDED(rv)) {
controllerCID = "43147b80-8a39-11d2-9938-0080c7cb1081";
appShell->CreateTopLevelWindow(parent, url, controllerCID,
window, observer, callbacks, width, height);
nsServiceManager::ReleaseService(kAppShellServiceCID,
appShell);
}
if (window != nsnull)
window->Show(PR_TRUE);
At time of writing, only url, window, observer
and callbacks are actually used. url is an URL describing
the contents of the window. window is set to the created window.
See the code for descriptions of the other parameters. As I write this I become
all embarrassed and want to encourage people to wait for a cleaner interface.
There is another very similar method nsIAppShellService::CreateDialogWindow().
It takes the same parameters and currently functions exactly the same. These
methods will differ only in the kind of border used on the window created. At
time of writing, this decision is made (the window is created) before any instructions
in the URL could be read and used to help make the decision.
At this time, window descriptions must be loaded from an URL. There will additionally be methods for loading windows from a stream. In fact, XPFE want to say the stream will be the preferred method, though the URL method will probably remain. Streams will disengage the toolkit further from the source of the window description, and will be our method for creating windows whose XUL is calculated at runtime, rather than being distributed as a file.
The JavaScript interface is an AppCore named ToolkitCore. It behaves
much like the standard JavaScript window.open() method. In fact,
one might wonder why we aren't using window.open() directly, and
one would have a point. At the moment, window.open() doesn't seem
to work, and probably doesn't make an nsWebShellWindow. XPFE are
anxious to get something working, and also expect to need closer control over
the window-opening process. We also paled at the prospect of stepping on the
JavaScript windowing code. But before we're done, we will need to reconcile
these two rather equivalent ways of opening a window.
So today, instantiating the Toolkit AppCore in the standard way and
executing its window-opening method (its only method at this time) will open
a new nsWebShellWindow.
function MakeDialog() {
var toolkitCore = XPAppCoresManager.Find("ToolkitCore");
if (!toolkitCore) {
toolkitCore = new ToolkitCore();
if (toolkitCore)
toolkitCore.Init("ToolkitCore");
}
if (toolkitCore)
toolkitCore.ShowWindow("resource:/res/samples/madedialog.xml",
window);
}
An XPAppCoresManager is pre-constructed; you can access it without
any more preparation than shown above. ToolkitCore is not; it requires
that small bit of preparation.
Between making and showing the window, there is a callback The resulting nsWebShellWindow
comes with an associated DOM content model. Any initial control settings or
content changes which must be made before the window is actually shown can be
done at this time.
This callback is any JavaScript specified as the value of the onConstruction
attribute of the <window> element. In the example below,
the window start tag is declared
<window ... onconstruction="Startup()">
During the onconstruction callback, dialog authors are free to
alter control settings from JavaScript:
function Startup() {
// yellowize and check the "ow" button
document.bgColor = "yellow";
var checkbox = ElementByID("ow"); // homespun function
if (checkbox)
checkbox.checked = true;
}
Except that not all properties are hooked up yet. The change to background color in the example won't do anything. But the important example does work!
When creating a window from C code (not JavaScript), the Toolkit Appcore need
not be used. Additional opportunities to perform dialog preprocessing are afforded
by the callbacks parameter to CreateTopLevelWindow. Dialog preprocessing
can also (theoretically) be done in C by defining an AppCore, loading and calling
it from the Startup() method, as the ToolkitCore AppCore
was called to create the window. This hasn't actually been tried yet, but it
"should be straightforward."
After the dialog has run -- a dismissal button has been clicked, say -- dialog users will need a callback in which to query the current control settings. As always, this will be done by walking the DOM content model, and accessed through a JavaScript hook.
We are uncertain whether a specific callback need be added: the event handler for the dismissal buttons may be entirely sufficient. But as the reader has no doubt guessed, there is no specific debriefing hook implemented at this time.
The following XUL describes a browser window with a simple toolbar containing a single button which will open another window: a nonmodal dialog, with a bit of imagination.
<?xml version="1.0"?>
<?xml-stylesheet href="xul.css" type="text/css"?>
<!DOCTYPE window>
<xul:window
xmlns:html="http://www.w3.org/TR/REC-html40"
xmlns:xul ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<html:script>
function MakeDialog() {
var toolkitCore = XPAppCoresManager.Find("ToolkitCore");
if (!toolkitCore) {
toolkitCore = new ToolkitCore();
if (toolkitCore)
toolkitCore.Init("ToolkitCore");
}
if (toolkitCore)
toolkitCore.ShowWindow("resource:/res/samples/madedialog.xml",
window);
}
</html:script>
<xul:toolbox>
<xul:toolbar>
<html:button html:onclick="MakeDialog()"
html:style="background-color:rgb(192,192,192);">
Make Dialog
</html:button>
</xul:toolbar>
</xul:toolbox>
</xul:window>
Note that only html widgets will have the expected html-specific DOM attributes like "checked" for checkboxes. XUL buttons will respond to their own APIs (to be documented in separate, dedicated papers), as well as to the core DOM interfaces.
The above code will produce a live window with a functional button if placed
in a file named, perhaps, makedialog.xml in the res/samples
subdirectory within the directory containing apprunner. (Placing
it in that directory allows it to find the stylesheet xul.css mentioned
in the stylesheet processing instruction.) Launch apprunner pointing
at that URL to load it into the main window.
apprunner -url resource:/res/samples/makedialog.xml
A suitable dialog description XUL file (named madedialog.xml in
the above code) will be loaded when the Make Dialog button is pressed.
The following example contains the linkup code mentioned in the main body of
this document and has been seen to work in recent builds of the application.
The authors have turned a jaundiced eye toward any sense of aesthetics in the design of this dialog. It's ugly. It will show up directly on top of the main "browser" window. I plan to rely on the estimable work of a colleague to provide some documentation for what sorts of visual effects can be accomplished with dialog controls and provide a link to a separate document with prettier samples.
<?xml version="1.0"?>
<?xml-stylesheet href="xul.css" type="text/css"?>
<!DOCTYPE window>
<xul:window
xmlns:html="http://www.w3.org/TR/REC-html40"
xmlns:xul ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xul:onconstruction = "Startup()">
<html:script>
// dialog initialization code
function Startup() {
var checkbox = ElementByID("remind");
if (checkbox)
checkbox.checked = true;
}
// OK button handler
function DoOK() {
// get checkbox
// (using a document method available on HTML and XUL
// documents, but not on XML documents)
var checkbox = document.getElementByID("remind");
if (checkbox) {
// load some hypothetical appcore interested in
// the outcome of this dialog
var donationsCore = XPAppCoresManager.Find("DonationsCore");
if (!donationsCore) {
donationsCore = new DonationsCore();
if (donationsCore)
donationsCore.Init("DonationsCore");
}
// tell the appcore about the new setting
if (donationsCore)
donationsCore.SetRemindFlag(checkbox.checked);
}
}
// find and return the DOM element with the given ID
// the equivalent of document.getElementByID(), but also
// works for XML documents (unused in the example)
function ElementByID(id) {
var element;
var ctr;
var taglist = document.getElementsByTagName("*");
element = null;
for (ctr = 0; ctr < taglist.length; ctr++)
if (taglist[ctr].getAttribute("id") == id) {
element = taglist[ctr];
break;
}
return element;
}
</html:script>
<table xmlns="http://www.w3.org/TR/REC-html40">
<tr>
<td>Give me your money</td>
</tr>
<tr>
<td>
<input type="checkbox" id="remind"/>Remind me
</td>
</tr>
<tr>
<td>
<button onClick="DoOK()">OK</button>
</td>
</tr>
</table>
</xul:window>
Note the window will not be dismissed when the OK button is pressed. The current state of the code will require that you hit "OK" and then the window close box.