Account confirmers, Anti-spam team, canmove, Confirmed users, Bureaucrats and Sysops emeriti
4,083
edits
No edit summary |
No edit summary |
||
(15 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
The architecture patch in [https://bugzilla.mozilla.org/show_bug.cgi?id=394522 bug 394522] paved the way for migrating SeaMonkey's old XPFE preference panels to toolkit's <prefpane>s. See [[SeaMonkey:Toolkit_Transition:PrefwindowPanes]] for migration coordination (who's doing what?). | The architecture patch in [https://bugzilla.mozilla.org/show_bug.cgi?id=394522 bug 394522] paved the way for migrating SeaMonkey's old XPFE preference panels to toolkit's <prefpane>s. See [[SeaMonkey:Toolkit_Transition:PrefwindowPanes]] for migration coordination (who's doing what?). | ||
If you have any suggestions or questions, you can ask in the newsgroup [news:mozilla.dev.apps.seamonkey mozilla.dev.apps.seamonkey] or join the IRC channel [irc://irc.mozilla.org/#seamonkey #seamonkey on MozNet]. Or email [mailto:mnyromyr@tprac.de me]. | If you have any suggestions or questions, you can ask in the newsgroup [news://news.mozilla.org/mozilla.dev.apps.seamonkey mozilla.dev.apps.seamonkey] or join the IRC channel [irc://irc.mozilla.org/#seamonkey #seamonkey on MozNet]. Or email [mailto:mnyromyr@tprac.de me]. | ||
== PrefWindow v5 (XPFE) vs. v6 ("new toolkit") == | == PrefWindow v5 (XPFE) vs. v6 ("new toolkit") == | ||
Line 18: | Line 18: | ||
* ''Edit → (Legacy Prefwindow...)'' opens the old XPFE ''pref.xul''. | * ''Edit → (Legacy Prefwindow...)'' opens the old XPFE ''pref.xul''. | ||
==== | ====Tree items==== | ||
Edit [http://mxr.mozilla.org/seamonkey/source/suite/common/pref/preferences.xul /suite/common/pref/preferences.xul] (or the respective overlay, if | Edit [http://mxr.mozilla.org/seamonkey/source/suite/common/pref/preferences.xul /suite/common/pref/preferences.xul] (or the respective overlay, if e.g. you're migrating a MailNews panel) and set the following attributes on the respective <treeitem>: | ||
<treeitem container="true" | <treeitem container="true" | ||
Line 34: | Line 34: | ||
* Set the ''url'' attribute to the chrome URI of the panel to be reached by this <treeitem>. | * Set the ''url'' attribute to the chrome URI of the panel to be reached by this <treeitem>. | ||
==== | To minimize confusion about which panels are migrated and which not, comment out unmigrated treeitems in the new dialog and replace the label in the legacy dialog with "(migrated)". ''This is not done yet in the new dialog!'' | ||
====Panels==== | |||
Old panels were actually "real" documents, loaded into a browser window. New panels are <prefpane> elements, overlayed over the main window. To avoid the global namespace clashes of <script>s mentioned above, SeaMonkey's prefwindow brings along some additional internal handling. | Old panels were actually "real" documents, loaded into a browser window. New panels are <prefpane> elements, overlayed over the main window. To avoid the global namespace clashes of <script>s mentioned above, SeaMonkey's prefwindow brings along some additional internal handling. | ||
Line 52: | Line 54: | ||
script="chrome://communicator/content/pref/pref-navigator.js"> | script="chrome://communicator/content/pref/pref-navigator.js"> | ||
* The ''id'' of the <prefpane> must the value of the ''prefpane'' attribute of the <treeitem> (see above). | * The ''id'' of the <prefpane> must be the value of the ''prefpane'' attribute of the <treeitem> (see above). | ||
* The ''label'' should have the same value as the the old <page>.''headertitle'' attribute. It will be shown above the panel. | * The ''label'' should have the same value as the the old <page>.''headertitle'' attribute. It will be shown above the panel. | ||
* The ''script'' attribute contains a whitespace delimited list of chrome URIs of the script files to be loaded into the panel's(!) scope. This means that the contents of these scripts will be accessible as members of the <prefpane> object, eg. ''document.getElementById("navigator_pane"). | * The ''script'' attribute contains a whitespace delimited list of chrome URIs of the script files to be loaded into the panel's(!) scope. This means that the contents of these scripts will (only) be accessible as members of the <prefpane> object, eg. ''document.getElementById("navigator_pane").DoSomething()''. | ||
* The ''onpaneload'' attribute can contain code which will be executed when the panel has finished loading, eg. ''this. | * The ''onpaneload'' attribute can contain code which will be executed when the panel has finished loading, eg. ''this.AfterPrefInit()''. | ||
The old call to ''parent.initPanel'' is not needed anymore, panel initialization is done by the backend. If the panel's loaded scripts contain the special Startup() function, it will be called automatically on panel load. | The old call to ''parent.initPanel'' is not needed anymore, panel initialization is done by the backend. If the panel's loaded scripts contain the special Startup() function, it will be called automatically on panel load. | ||
==== | Although the script files are loaded into the object context of the <prefpane>, there is no need to qualify each call etc. inside with <tt>this</tt> — the subScriptLoader does handle this automatically! Please include a note in the script file about which <prefpane> is the target, eg. like in ''pref-navigator.js'': | ||
// The contents of this file will be loaded into the scope of the object | |||
// <prefpane id="navigator_pane">! | |||
====Preferences==== | |||
Preferences handling required some complicated hack-arounds, this has changed. | Preferences handling required some complicated hack-arounds, this has changed. | ||
Line 80: | Line 87: | ||
<overlay> | <overlay> | ||
<prefpane> | <prefpane> | ||
... | |||
<checkbox id="homeButton" | |||
label="&homeButton.label;" | |||
accesskey="&homeButton.accesskey;" | |||
preference="browser.toolbars.showbutton.home"/> | |||
... | |||
<preferences id="navigator_preferences"> | <preferences id="navigator_preferences"> | ||
<preference id="browser.toolbars.showbutton.home" | <preference id="browser.toolbars.showbutton.home" | ||
Line 86: | Line 99: | ||
... | ... | ||
</preferences> | </preferences> | ||
The <preferences> element must be a direct child of the <prefpane>, with the <preference>s as its children. Each <preference> needs the attributes ''name'' (with the name of the preference in ''prefs.js'' etc.) and ''type'' with its type, see [http://developer.mozilla.org/en/docs/Preferences_System:preference DevMo for details]. Since preference names are unique by default, the ''id'' attribute should be set to the pref name as well. | The <preferences> element must be a direct child of the <prefpane>, with the <preference>s as its children. (Usually, it's the first child of the <prefpane>, so that the <preference>'s constructor can init any elements watching this pref. Note the order of initialization code below, though.) Each <preference> needs the attributes ''name'' (with the name of the preference in ''prefs.js'' etc.) and ''type'' with its type, see [http://developer.mozilla.org/en/docs/Preferences_System:preference DevMo for details]. Since preference names are unique by default, the ''id'' attribute should be set to the pref name as well. | ||
The former ''prefstring'' attribute on an element must be replaced by a ''preference'' attribute, which contains the ''id'' of the respective <preference>. | The former ''prefstring'' attribute on an element must be replaced by a ''preference'' attribute, which contains the ''id'' of the respective <preference>. | ||
All reading/writing of preferences is handled by the backend, there is usally no need to instantiate preference interface/service! | All reading/writing of preferences is handled by the backend, there is usally no need to instantiate preference interface/service! | ||
====Order of Initialization Code==== | |||
* On opening the preferences dialog, the prefwindow binding gets attached to the <prefwindow> element. The toolkit base binding's constructor is executed and dispatches 'paneload' events to any static <prefpane>s. | |||
* On changing the panel selection, either by code or tree selection, a 'paneload' event is only dispatched to the new <prefpane> if it hasn't been initialized yet. | |||
These 'paneload' events are processed in the following order: | |||
* SeaMonkey's derived <prefpane> 'paneload' handler is called, loading the scripts specified in the <prefpane>'s 'script' attribute. If defined, the <prefpane>'s Startup() function is called, so that <preference> elements can be created dynamically.<br><i style="color:red">Mind that UI elements tied to <preference>s are '''not''' yet initialized to their <preference> value here!</i> Instead, simply read the value directly from the <preference> element. | |||
* Toolkit's <prefpane> 'paneload' handler is called and initializes the <preference> based UI elements. Any 'onsyncfrompreference' handlers attributes on such UI elements are evaluated. (See [https://bugzilla.mozilla.org/show_bug.cgi?id=308754 bug 308754] for problems with these handlers.) | |||
* SeaMonkey's <prefwindow> 'paneload' handler is called, synchronizing <tree> and <prefpane>. | |||
* Finally, any <prefpane> 'onpaneload' handler attribute is evaluated. | |||
====Reacting dynamically to user input and preference changes==== | |||
Under the v5 prefwindow it was very common for changes in one preference to affect the ability to use another preference. Since the preferences were a snapshot taken when the pane first loaded, the only synchronisation necessary was when the user interacted with the controlling preference. | |||
However unlike the v5 preferences, the v6 preferences support instant apply of preferences. This means that the preference elements need to keep themselves up-to-date with external preference changes so that the user can be sure they are seeing the current value of the preference. Synchronisation must therefore be done whenever the preference changes, whether or not this is due to a direct user interaction. | |||
Fortunately this is usually simply achieved by moving the oncommand or similar handler to an onchange handler on the <preference> element itself. | |||
== Accessibility markup enhancements == | |||
While migrating the preferences, all XUL files should be checked for accessibility markup, correct/consistent access keys, and other related changes. | |||
=== Properly labelling controls === | |||
Associating a XUL:label element with the XUL element it is labelling can be done in one of two ways: | |||
For simple labelling, use the "control" attribute of the XUL:label element and set its value to the ID of the XUL element it is labelling. This has two effects: | |||
* It associates the XUL element's accessible with the name/value of the label it is associated with. | |||
* When clicking on the label, the associated control gains focus. | |||
This is good for most label/control combinations. XUL elements that can be used are textbox, menulist, radiogroup or other similar controls. Elements like buttons, checkboxes or radio elements that have a "label" attribute usually don't need labelling, since their label provides the necessary info. | |||
The other way to label a control is to use the "aria-labelledby" attribute on the XUL control that is to be labelled. The "aria-labelledby" attribute's value consists of one or more IDs of other elements that make up the label. An example is the "keep history for x days" combination. There is one label in front of the textbox "keep history for", then there's the value of days inside the textbox, and another label following that only contains the "days" appendix. If we used the "control" attribute on the preceeding label, the label would only be "Keep history for". However the user would not know what: Days, Months, Eons? So we use a little trick borrowed from Web 2.0 accessibility technology: We make up the label out of the first label, followed by the contents of the textbox, followed by the trailing label. The IDs of these are to be enclosed in quotes and separated by spaces. Of course, the two labels will need to get IDs for this to work. | |||
"aria-labelledby" can also be used if you actually need a XUL:description element to label something (because you might need wrapping). Instead of giving the XUL:description element the "control" attribute, give it an ID instead, and give the control it is labelling an "aria-labelledby" attribute with the ID of the XUL:description element that is labelling it. If we used the "control" attribute on the XUL:description, it would become the AccDescription property of the control's accessible, which is usually used to give additional information rather than the main name. | |||
You can also use "aria-labelledby" on a textbox that follows a checkbox or radio button, and set it to the checkbox's or radio button's ID. This causes the checkbox's or radio button's caption to become the accessible name of the textbox. | |||
An example taken from the new History preferences pane: | |||
old: | |||
<label value="&pageHis.label;" | |||
accesskey="&pageHis.accesskey;" | |||
control="histDay"/> | |||
<textbox id="histDay" | |||
size="3" | |||
preference="browser.history_expire_days"/> | |||
<label value="&days.label;"/> | |||
Note that this has already a simple label/textbox association. However it can be improved like this: | |||
<label value="&pageHis.label;" | |||
accesskey="&pageHis.accesskey;" | |||
control="histDay" | |||
id="histHeadLabel"/> | |||
<textbox id="histDay" | |||
size="3" | |||
preference="browser.history_expire_days" | |||
aria-labelledby="histHeadLabel histDay histTrailLabel"/> | |||
<label value="&days.label;" | |||
id="histTrailLabel"/> | |||
The accessible name for the textbox will become the first label, followed by the value currently inside the textbox, followed by the trailing label, giving the control a much more meaningful name. Keeping the "control" attribute on the label ensures that, if it is clicked, the textbox gets focus. "aria-labelledby" will, if present, override the label/textbox association for the accessible name. | |||
Quoting [https://bugzilla.mozilla.org/show_bug.cgi?id=429143#c17 bug 429143 comment 17]: | |||
MarcoZ> IanN: Well, you can accomplish the wanted without ARIA, so use | |||
conventionals. ARIA should be used as a last resort, or with funny constructs | |||
where the label of a checkbox is the label for an adjacent textbox at the same | |||
time. So, use the label first, then the textbox, then the description, and | |||
point both control attributes to the textbox. | |||
=== Checking access keys === | |||
Access Keys are a good way to give quick access to the controls of a dialog. Usually the ALT key is being held down while the underlined letter, the letter we specify as the access key, is being pressed. A few things to note: | |||
* The access key should obviously be part of the control's caption/label. | |||
* Access keys in multipage dialogs such as the new pref window do not need to be unique across the whole dialog, they just need to be unique on a single pane. So while you should avoid having the letter "g" associated with two controls on the same pane, having the letter "g" on two different panes is no problem. | |||
[[category:SeaMonkey|t]] |
edits