作者:galeki

目录

(一) :插件的基本结构
(二) :用XUL创建窗口控件
(三) :排列窗口控件
(四) :更多的窗口控件
(五) :驱动XUL界面
(六) :关于event对象
(七) :键盘快捷键
(八) :控件激活
(九) :command元素

(一) : 扩展的基本结构

用过firefox的人肯定要安装firefox的扩展,这样才能发挥火狐的全部实力。一般扩展是一个后缀为.xpi的文件,其实这个文件就是zip格式的压缩包,压缩了一个扩展所需要的所有目录和文件,基本的目录结构如下:

extension.xpi:
/install.rdf
/components/*
/components/cmdline.js
/defaults/
/defaults/preferences/*.js
/plugins/*
/chrome.manifest
/chrome/icons/default/*
/chrome/
/chrome/content/

看似很复杂,让我们从最重要的文件开始介绍起:

1. install.rdf

从名字可以看出,这个文件描述了扩展安装所需要的信息,包括了扩展的标识、版本、适用的应用程序、作者等等等等。本身是一个rdf文件。

基本的install.rdf示例:

<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:em="http://www.mozilla.org/2004/em-rdf#"> 

<!-- 标识和版本 -->
<Description about="urn:mozilla:install-manifest">
    <em:id>galeki@linuxge.org</em:id>
    <em:version>1.0</em:version>
    <em:type>2</em:type> 

    <!-- 针对的应用程序 -->
    <em:targetApplication>
    <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>1.5</em:minVersion>
        <em:maxVersion>2.0.0.*</em:maxVersion>
    </Description>
    </em:targetApplication> 

    <!-- 名称、介绍、作者和网站地址 -->
    <em:name>Hello</em:name>
    <em:description>扩展测试</em:description>
    <em:creator>galeki</em:creator>
    <em:homepageURL>http://blog.linuxgem.org/galeki</em:homepageURL>
</Description>
</RDF>

8~10行是标识信息,第8行的<em:id>段是此软件包唯一的标识,可以是作者的email,目的是唯一标识此软件包。第9行是目前的版本。第10行的<em:type>,值为2代表”我是扩展”,4代表”我是主题”,这里是扩展,所以值为2。

14~20行的<em:targetApplication>段描述了软件包针对的应用程序,16行那段古怪的<em:id>表明此扩展是针对firefox的,而不是thunderbird、sunbird,所以,只要是针对firefox的扩展,此id的值都不变。17~18行描述了此扩展适用的firefox版本。

23~26行描述了扩展的基本信息,从标签的名称上就可以看出意思,扩展管理器中显示的扩展信息,就是来源于这里。

2. chrome目录

chrome是扩展最重要的一个目录,所有描述扩展的文件,称为chrome。

chrome一般包括3类文件,content、locale、skin:

content:描述扩展界面、外观的定义文件,比如按钮布局,对话框,通过XUL文件和js文件来设定,后面我们可以看到XUL的介绍。
locale: 描述多语言信息的文件,一般是xml的dtd文件。
skin: 定义界面样式的css文件和所需要的图片文件。

一个典型(并不绝对)的chrome目录下包含这么几个子目录:

/chrome
content/
locale/
skin/

三个子文件夹存放了扩展对应的content、locale和skn类型的文件。

3.chrome.manifest文件

接下来,得让fierfox找到扩展的content、locale、skin文件在哪里,chrome.manifest就是这个作用。

这个文件让firefox知道扩展的content、locale、skin等文件在哪里,例如:

content hello chrome/content/

简单来说,就是指明,hello这个扩展的content类型文件,在chrome目录的content子目录下。

locale的定义稍有区别:

locale hello zh-CN chrome/locale/zh-CN/

指明hello这个扩展的zh-CN,也就是中文的locale,在chrome/locale/zh-CN/目录下。

skin的定义和locale相似:

skin hello classic/1.0 chrome/skin/classv1/

指明hello这个扩展,名叫classic/1.0的skin,在chrome/skin/classv1/目录下。

通过chrome.manifest文件的指定,firefox就可以找到对应的文件。在firefox中,引用web服务器上的文件是用的 “http://”前缀,引用硬盘上的文件是用的“file://”前缀,引用扩展中的文件有单独的引用前缀,就是“chrome://“,比如:

chrome://hello/content/about.xul

引用了hello扩展content类型文件中的about.xul文件。这样一来,不管在硬盘上扩展的content、locale、skin是在什么地方,只要chrome.manifest文件配置好,就可以让firefox通过“chrome://”引用到特定的文件了。

FireFTP的例子:
Firefox扩展开发01
由上图的地址栏可以看出,打开FireFTP这个扩展,实际上就是打开了fireftp的content类型文件中的fireftp.xul文件。

(二):用XUL创建窗口控件

在Firefox中,所有的界面都是基于XUL文件描述的,XUL本身是一个XML文件。就连Firefox本身,也是基于XUL的,不信的话,可以在firefox的地址栏中敲入: chrome://browser/content/browser.xul,看看会出现什么情况吧。

1.创建一个简单的窗口

多说无益,让我们看一个简单的XUL文件示例:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

</window>

把上述的代码保存为test.xul,然后在firefox的“文件” –> “打开文件” 中打开这个文件,就可以看到(下图)。

哎,怎么啥都没有? 因为我们还没有往窗口中加入任何东西,所以除了标题栏之外,是一片空白。实际上,上面的XUL文件,是每个窗口的基本框架。

第1行是XML文件的基本表示,第2行引用渲染窗口控件的样式表文件,这里我们先跳过对这行的解释,只要知道“chrome://global/skin/” 引用的是firefox全局默认的样式表即可。

第3行的<window>元素,就是窗口的根元素,你可以把它想象成HTML中的<html>元素。属性id的值可以随便取,和HTML中的id属性相同,必须要保证全局唯一,因为之后我们要通过id来引用每个窗口。title属性就是窗口的标题,xmlns是名称空间,说明之下的内容是XUL。

窗口中所有的内容,都要放在<window>和</window>之间,就像HTML中所有的元素必须放在<html>和</html>之间一样。
Firefox扩展开发02
2.让我们往窗口里添点东西吧

所有一般程序具有的窗口控件(按钮、单选复选框、文本输入框、下拉菜单……),在firefox的窗口中都可以实现,只不过,不同的窗口控件在XUL中变成了不同的XML标签,控件的属性(大小、文本、排列方式……)变成了标签的属性值而已。

2.1按钮

我们先来添加个按钮,打开test.xul,添加下面的代码:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <button label="普通的按钮"/> 

</window>

按钮在XUL中就是<button>这个标签,label属性为按钮上显示的文字。
Firefox扩展开发03
2.2文字

在窗口上显示的文字,用<label>标签来显示:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <label value="下面是一个普通的按钮:)"/>
        <button label="普通的按钮"/> 

</window>

Firefox扩展开发04
2.3文本输入框

文本输入框,为<textbox>标签。让我们清除刚才添加的代码,下面的代码显示了<textbox>的几种用法。

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <label value="用户名"/>
        <textbox id="username"/>
        <label value="密码"/>
        <textbox id="password" type="password" maxlength="10"/> 

        <label value="个人简介"/>
        <textbox multiline="true"
            value="在这里填入你的个人简介。"/> 

</window>

第11行,密码输入框要设置type属性为”password”;第13行,如果需要多行的输入框,需要指定multiline为”true”。
Firefox扩展开发05
2.4单选和复选框

单选框为<radio>,复选为<checkbox>。

单选框的分组,只要把单选框标签包含在<radiogroup>中即可。看代码:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <label value="用户名"/>
        <textbox id="username"/>
        <label value="密码"/>
        <textbox id="password" type="password" maxlength="10"/>
        <label value="你喜欢的颜色"/>
        <radiogroup>
            <radio id="blue" label="蓝色"/>
            <radio id="yellow" selected="true" label="黄色"/>
            <radio id="red" label="红色"/>
        </radiogroup>
        <checkbox label="保存我的信息"/>
        <label value="个人简介"/>
        <textbox multiline="true"
            value="在这里填入你的个人简介。"/> 

</window>

Firefox扩展开发06
2.5下拉选项

      <?xml version="1.0"?>
      <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
      <window
        id="test-window"
        title="测试用的窗口"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

              <menulist>
                <menupopup>
                  <menuitem label="红色"/>
                  <menuitem label="蓝色"/>
                  <menuitem label="黄色" selected="true"/>
                </menupopup>
              </menulist> 

      </window>

每个选项包含在<menuitem>中,<menuitem>再被包含在<menupopup>和<menulist>中。
Firefox扩展开发07
2.6进度条

用<progressmeter>表示。进度条分为两类,一种是有明确完成进度的,一种是没有完成进度的,通过设置mode=”determined”和
mode=”undetermined”来实现。

看一下效果就明白了:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <progressmeter
          id="progress1"
          mode="determined"
          value="60%"/>
        <progressmeter
          id="progress2"
          mode="undetermined"/> 

</window>

Firefox扩展开发08
至此,我们介绍了用XUL创建出一个窗口所具备的基本控件的方法,但是这些空间只能按照次序从上到下的显示,而且,每个元素将占满窗口的整个宽度,接下来,我们要看一下怎么布局窗口中众多的控件。

(三) : 排列窗口控件

上一篇我们讲到了用XUL创建基本的窗口控件(按钮、输入框、单选复选框……),现在我们来讲一下如何排列他们。

盒子:<hbox>与<vbox>

XUL中主要的布局元素成为”盒子”,分为两种,水平盒子和垂直盒子,也就是<hbox>和<vbox>,说白了就是把包含在盒子内的空间水平或者垂直排列,如果你熟悉GTK+编程的话,一定对这两种布局方式非常的熟悉。

上一篇的控件,只能按照顺序垂直分布在窗口中,因为这是窗口默认的排列控件的方式,要想改变,就要把控件放在盒子中:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <vbox>
            <hbox>
                <label value="用户名:"/>
                <textbox id="login"/>
            </hbox>
            <hbox>
                <label value="密码:"/>
                <textbox id="pass"/>
            </hbox>
            <button id="ok" label="登录"/>
            <button id="cancel" label="取消"/>
        </vbox>
</window>

和上一篇一样,把上述文件保存为test.xul,并用firefox打开。
Firefox扩展开发09
运行得不错,不过,“密码:”旁边的输入框似乎靠的太近了些,我们可以把两个文字标签、两个输入框,分别放在两个个<vbox>中,这样就解决了对齐问题:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <vbox>
            <hbox>
                <vbox>
                    <label value="用户名:"/>
                    <label value="密码:"/>
                </vbox>
                <vbox>
                    <textbox id="login"/>
                    <textbox id="pass"/>
                </vbox>
            </hbox>
            <button id="ok" label="登录"/>
            <button id="cancel" label="取消"/>
        </vbox>
</window>

显示效果:
Firefox扩展开发10
盒子内的布局

当我们把上面的窗口拖大,窗口控件还是停留在窗口的左边,留下右边一大片空白,这似乎不是我们想要的效果:
Firefox扩展开发11
我们可以在<vbox>或<hbox>中的pack属性来控制,pack有3种值:

1. start:对vbox来说,是盒内全部靠上,对hbox,就是盒内全部靠左。
2. center:盒内居中。
3. end:vbox是靠下,hbox是靠右。

这里,我们还要介绍一个flex属性,默认情况下,盒子的大小是不变的,等于盒内元素的总大小,但是当flex属性为”1″时,盒子的大小是随着窗口的增大而增大,这样才能通过设置pack属性控制盒内的布局:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <vbox>
            <hbox pack="center" flex="1">
                <vbox>
                    <label value="用户名:"/>
                    <label value="密码:"/>
                </vbox>
                <vbox>
                    <textbox id="login"/>
                    <textbox id="pass"/>
                </vbox>
            </hbox>
            <hbox pack="center" flex="1">
                <button id="ok" label="登录" />
                <button id="cancel" label="取消" />
            </hbox>
        </vbox>
</window>

这样就实现了居中:
Firefox扩展开发12
分组窗口控件

有的时候,窗口中一部分空间是相互关联的,为了表示出这种关联关系,要用到<groupbox>:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <groupbox>
            <caption label="9月20日是……?"/>
            <label value="植树节"/>
            <label value="爱牙日"/>
            <label value="中秋节"/>
            <label value="元宵节"/>
        </groupbox>
</window>

显示效果:
Firefox扩展开发13
<caption>的值,就是分组标签标题的值,<caption>甚至可以包含其他的控件:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <groupbox>
            <caption>
                <checkbox label="Enable Backups"/>
            </caption>
            <hbox>
                <label control="dir" value="Directory:"/>
                <textbox id="dir"/>
            </hbox>
            <checkbox label="Compress archived files"/>
        </groupbox>
</window>

显示效果:
Firefox扩展开发14

(四) : 更多的窗口控件

标签盒子

标签盒子是啥?大家都见过,就是分页标签:
Firefox扩展开发15
对应的代码:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <tabbox>
            <tabs>
                <tab label="第一个标签标题"/>
                <tab label="第二个标签标题"/>
            </tabs>
            <tabpanels>
                <tabpanel id="firsttab">
                    <label value="第一个标签的内容"/>
                </tabpanel>
                <tabpanel id="secondtab">
                    <label value="第二个标签的内容"/>
                </tabpanel>
            </tabpanels>
        </tabbox>
</window>

每个标签盒子中的内容被包含在<tabbox>中,<tabs>下的<tab>包含标签标题,<tabpanels>下的<tabpanel>包含每个标签的内容,按顺序和标签标题<tab>对应。

iframe

<iframe>这个标签在HTML里再熟悉不过了,在XUL中,作用和用法HTML一样,可以用来在窗口中嵌套一个网页,只要设置<iframe>的src属性即可:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <label value="以下为GemBlog首页…………"/>
        <iframe id="content" src="http://blog.linuxgem.org" flex="1"/>
</window>

效果:
Firefox扩展开发16
工具栏

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <toolbox>
            <toolbar>
                <toolbarbutton label="< 后退"/>
                <toolbarbutton label="前进 >"/>
                <textbox id="url"/>
            </toolbar>
        </toolbox>
</window>

<toolbox>包含<toolbar>,<toolbar>包含工具栏中的元素,<toolbarbutton>表示工具栏按钮:
Firefox扩展开发17
菜单栏

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
        <menubar>
            <menu label="文件">
                <menupopup>
                    <menuitem label="新建"/>
                    <menuitem label="打开"/>
                    <menuitem label="保存"/>
                    <menuseparator/>
                    <menuitem label="退出"/>
                </menupopup>
            </menu>
            <menu label="编辑">
                <menupopup>
                    <menuitem label="拷贝"/>
                    <menuitem label="粘贴"/>
                </menupopup>
            </menu>
        </menubar>
</window>

<menubar>为一个菜单栏的顶极标签,菜单栏中的每一项为一个<menu>,每一个菜单项下面的子项目,为<menupopup>下的<menuitem>,<menuseparator>为分隔符。

效果:(不知咋的,打开菜单的状态下没法抓图 :( )
Firefox扩展开发18

(五) : 驱动XUL界面

前面几节,讨论了怎么用XUL创建窗口控件,这样我们就能创建出漂亮的程序界面,但是现在点击窗口中的控件,却什么也不会发生,接下来我们来看看怎样驱动界面中的控件。

大家都知道,在HTML中,同样也有简单的控件,比如按钮、单选、复选框,主要用HTML中的<input>标签来实现,HTML中是怎样驱动这些控件的呢?一般是通过诸如onclick、onfocus、onmouseover等等事件属性,通过事件驱动,再配合javascript 来完成,比如:

<input type="button" onclick="alert('我被点击了!')" />

如上代码在HTML中创建一个普通的按钮,点击它时,弹出“我被点击了!”的对话框。

在XUL中,驱动控件的方法和HTML基本相同,并且,也是通过javascript来完成,在XUL里,最有用的事件属性是oncommand,当控件被激活时就会出发这个事件,比如按钮被点击,单选复选框被点击,菜单项被点击等等,比如:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <label value="下面是一个普通的按钮:)"/>
        <button label="普通的按钮" oncommand="alert('我被点击了!')"/> 

</window>

点击按钮,和HTML中一样,弹出窗口显示:
Firefox扩展开发19
通过javascript的事件冒泡,可以在上层元素中捕获下层元素的事件信息,并进行识别处理:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <vbox oncommand="alert(event.target.tagName + ' 被点击');">
            <button label="普通的按钮"/>
            <checkbox label="我是单选框"/>
        </vbox> 

</window>

当事件被激活,event.target中包含了被激活对象的信息,其中tagName就是对象标签的名称,可以通过它识别事件实在哪个标签(也就是控件)上被激活的。效果如下:
Firefox扩展开发20
在XUL中,负责事件处理的javascript代码,可以像上面那样内联在xml标签中,也可以像HTML那样,从外部的js文件中引用:
button.js 文件:

function show_ok()
{
    alert("我被点击了!");
}

在XUL文件中,用<script>标签引用外部的js文件:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <script src="button.js" />
        <label value="下面是一个普通的按钮:)"/>
        <button label="普通的按钮" oncommand="show_ok()"/> 

</window>

效果和第一张图片一样。

你也可以通过熟悉的getElementById函数取得想要的对象,然后再用addEventListener函数动态的为元素添加事件处理函数,这样的好处就是完全分开了XUL和javascript,负责不同功能的两种语言不再纠缠在一起:
my_button.js 文件:

function show_ok()
{
    alert("我被点击了!");
}
var button = document.getElementById("my_button");
button.addEventListener('command', show_ok, true);
/*
 * 参数1为事件名称,oncommand即为command
 * 参数2为事件处理函数
 * 参数3为true 表示不捕捉冒泡消息
 */
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <label value="下面是一个普通的按钮:)"/>
        <button label="普通的按钮" id="my_button"/>
        <script src="my_button.js" /> 

</window>

效果和上面一样。

值得注意的是,首先得在XUL中指定对象的id属性,<script>标签也得在想引用的元素后面出现才可以,因为如果在前面,那时候下面的元素还没有被创建,getElementById也找不到它。

(六) : 关于event对象

上一篇我们看到了如何用javascript驱动界面上的控件,现在我们看看在事件被触发时,如何获得更详细的信息。

每当某个事件被触发(比如控件被点击或激活、鼠标移动到控件上等等),有关这个事件的详细信息都被储存到event对象中,并可以在事件处理函数中进行查看,比如上一篇中的第二个例子,就是通过 event.target.tagName 获得被激活的控件的标签名称,event还有更多的属性,让我们看个例子:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

<script>
    function ShowMouseXY(event){
        var document_xy = "X:" + event.clientX + " Y:" + event.clientY;
        var screen_xy = "X:" + event.screenX + " Y:" + event.screenY;
        document.getElementById("document").value = "鼠标在文档中的位置:  " + document_xy;
        document.getElementById("screen").value = "鼠标在屏幕中的位置:  " + screen_xy;
    }
</script> 

        <hbox>
            <label id="screen"/>
            <label id="document"/>
        </hbox>
        <hbox width="800" height="600" onmousemove="ShowMouseXY(event);"/> 

</window>

这里用到了4个event中的属性,clientX、clientY、screenX、screenY,分别是事件触发时,鼠标相对文档的xy坐标值和相对整个屏幕的xy坐标值,第21行在hbox上设置了onmousemove的事件属性,每当鼠标在这个hbox上移动,就会触发函数显示当前鼠标的位置(鼠标没有被抓下来):
Firefox扩展开发21
上一篇的第二个例子,我们用event.target引用了当前被激活的控件,通过事件冒泡在vbox上识别,与其相对的,还有一个event.currentTarget属性,引用的是事件冒泡最终传递给的控件:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script>
    function who_am_i(event)
    {
        message = "我是" + event.currentTarget.tagName + "。你点击了" + event.target.tagName;
        alert(message);
    }
</script>
        <vbox oncommand="who_am_i(event)">
            <button label="OK"/>
            <checkbox label="Save backup"/>
        </vbox> 

</window>

显示效果:
Firefox扩展开发22

(七) : 键盘快捷键

键盘快捷键是一个应用程序不可缺少的部分,最常见的地方在菜单栏中,在XUL中添加键盘快捷键是很简单的,只需要设置控件的accesskey属性即可:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口" 

        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

            <menubar>
                <menu id="file-menu" label="文件(F)" accesskey="f">
                    <menupopup id="file-popup">
                        <menuitem id="close-command" label="关闭(X)" accesskey="x"/>
                    </menupopup>
                </menu>
            </menubar> 

</window>

当然,accesskey属性也可以设置在其他控件上(比如按钮)。

如果要设置全局快捷键,这个时候就要用到<key>标签,每个<key>标签设置一个快捷键,最后把所有的<key>标签包含在一个<keyset>标签中即可:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <keyset>
            <key id="key1" modifiers="control" key="Q" oncommand="alert('你按了快捷键crtl+Q')"/>
            <key id="key2" modifiers="control alt" key="C" oncommand="alert('你按了快捷键crtl+alt+C')"/>
            <key id="key3" keycode="VK_F6" oncommand="alert('你按了快捷键F6')"/>
        </keyset> 

</window>

这里介绍了设置全局快捷键的3种情况:

第9行,设置的快捷键是crtl+q,modifiers就是通常所说的修饰键,modifiers可以取control、alt、meta、shift,分别代表了键盘上的ctrl、alt、meta、shift键。

第10行,设置的快捷键是ctrl+alt+c,演示了如何设置有两个修饰键的情况,只需把他们用空格分开即可。

第11行,设置的快捷键是F6,值得注意的是,前面两行中指示快捷键的属性是key,直接指出键盘上的字母值,但是碰到像F1、F2这样的功能键,还有TAB和回车这样的按键,就没法通过key属性设置了,这个时候只有通过keycode设置,其实,键盘上的每个键都对应了一个keycode值,这个值一般以“VK_”开头,下面的表格列出了常用的keycode值,对应的按键从后缀就可以看出:

VK_CANCEL VK_BACK VK_TAB VK_CLEAR
VK_RETURN VK_ENTER VK_SHIFT VK_CONTROL
VK_ALT VK_PAUSE VK_CAPS_LOCK VK_ESCAPE
VK_SPACE VK_PAGE_UP VK_PAGE_DOWN VK_END
VK_HOME VK_LEFT VK_UP VK_RIGHT
VK_DOWN VK_PRINTSCREEN VK_INSERT VK_DELETE
VK_0 VK_1 VK_2 VK_3
VK_4 VK_5 VK_6 VK_7
VK_8 VK_9 VK_SEMICOLON VK_EQUALS
VK_A VK_B VK_C VK_D
VK_E VK_F VK_G VK_H
VK_I VK_J VK_K VK_L
VK_M VK_N VK_O VK_P
VK_Q VK_R VK_S VK_T
VK_U VK_V VK_W VK_X
VK_Y VK_Z VK_NUMPAD0 VK_NUMPAD1
VK_NUMPAD2 VK_NUMPAD3 VK_NUMPAD4 VK_NUMPAD5
VK_NUMPAD6 VK_NUMPAD7 VK_NUMPAD8 VK_NUMPAD9
VK_MULTIPLY VK_ADD VK_SEPARATOR VK_SUBTRACT
VK_DECIMAL VK_DIVIDE VK_F1 VK_F2
VK_F3 VK_F4 VK_F5 VK_F6
VK_F7 VK_F8 VK_F9 VK_F10
VK_F11 VK_F12 VK_F13 VK_F14
VK_F15 VK_F16 VK_F17 VK_F18
VK_F19 VK_F20 VK_F21 VK_F22
VK_F23 VK_F24 VK_NUM_LOCK VK_SCROLL_LOCK
VK_COMMA VK_PERIOD VK_SLASH VK_BACK_QUOTE
VK_OPEN_BRACKET VK_BACK_SLASH VK_CLOSE_BRACKET VK_QUOTE
VK_HELP

显示效果:
Firefox扩展开发23

(八) :控件激活

当我们用鼠标点击一个控件,或者用TAB键移动到一个控件上时,我们说这个控件被激活了(focus),离开这个控件时,我们说这个控件失去焦点(blur),熟悉js的人一定知道 onfocus 和 onblur 这两个事件属性,XUL中也是一样,通过这两个属性控制控件在被激活和失去焦点时要做的事情。

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window
    id="test-window"
    title="测试用的窗口"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    <script>
        function displayFocus(){
            var elem=document.getElementById('username');
            elem.setAttribute('value','输入你的用户名,只能是英文。');
        }
    </script> 

        <label value="用户名:"/>
        <textbox id="login"  onfocus="displayFocus();"/>
        <description id="username" value="" />
</window>

显示效果:
Firefox扩展开发24
通过设置textbox的onfocus属性,在输入框被激活的时候,在下方显示提示信息。

接下来,我们看看如何获得当前用户激活的控件。

当前被激活的控件保存在 document.commandDispatcher 这个对象中的 focusedElement 属性中(后面的章节会详细介绍),那么如果用户改变了焦点,我们只要重新获取这个值,就可以得到当前激活的控件了。

(五) : 驱动XUL界面中介绍了 addEventListener 这个方法,通过 xxx.addEventListener(…) ,就可以给xxx控件添加事件处理函数,如果直接调用 addEventListener ,就是给全局的某个事件添加事件处理函数,既然我们要在用户激活控件时重新获得focuusedElement属性,那么我们只要处理全局的focus事件即可。

废话不说,看代码:

<window id="focus-example" title="测试窗口"
    onload="init();"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <script>
            function init(){
                addEventListener("focus",setFocusedElement,true);
        } 

            function setFocusedElement(){
                var focused = document.commandDispatcher.focusedElement;
                document.getElementById("focused").value = "你点了 " + focused.tagName;
        }
        </script> 

        <hbox>
            <label value="用户名:"/>
            <textbox id="username"/>
        </hbox> 

        <button label="我是按钮"/>
        <checkbox label="我是复选框"/> 

        <label id="focused" value="你啥也没点……"/> 

</window>

第2行,设置window的onload属性,和在js中设置 window.onload=”init();” 一样,在窗口装载完毕执行init()。

那么,我们看看init()执行了什么,第7行,通过addEventListener,将全局的focus事件关联到setFocusedElement() 这个函数上。

在setFocusedElement 函数中,第11行,直接通过 document. commandDispatcher. focusedElement 获得当前被激活的控件对象。

显示效果:
Firefox扩展开发25

(九) : command元素

何为command元素?从名字来看似乎和执行的命令有关,先来看个简单例子:

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window id="example-window" title="测试的窗口"
    xmlns:html="http://www.w3.org/1999/xhtml"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

<command id="cmd_openhelp" oncommand="alert('Help!');"/>
<button label="Help" command="cmd_openhelp"/>
</window>

第7行就是command元素,每个command元素一般有一个id属性,唯一的标识这个command对象,为了不容易和一般元素的id相冲突,在前面加一个cmd_的前缀是个不错的办法;oncommand属性指定了和这个command对象关联的命令动作。

第8行的button元素就引用了command元素关联的命令动作,在原来的章节中,是通过oncommand属性给控件关联动作,现在只需要用command属性,并把对应的command元素id作为属性值,就可以关联到特定的动作。

看样子我们好像是兜了个大圈子,兜这个圈子有什么好处呢?

* 首先,可以把表示命令动作的command元素单独保存在一个文件中,从而实现表示界面和表示显示的代码分离,更加容易管理。
* 另外,如果某些按钮、菜单项、工具栏按钮执行的都是一个动作那么我们主要把他们关联到相同的command元素上即可,而不用重复写好几遍。

而且不仅如此,下面的才是关键:

* 我们可以disable和enable一个command,如果command元素被设置成disable,那么和它关联的动作命令不会得到执行。
* disable和enable某个command元素的同时,和这个command元素关联的控件会被自动设置成disable和enable的状态。

看个了例子:

<window id="focus-example" title="测试窗口"
    onload="init();"
    xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 

        <command id="cmd_openhelp" oncommand="alert('这是帮助');"/>
        <button label="帮助" command="cmd_openhelp"/>
        <button label="还是帮助" command="cmd_openhelp"/> 

        <button label="禁用帮助"
            oncommand="document.getElementById('cmd_openhelp').setAttribute('disabled','true');"/>
        <button label="激活帮助"
            oncommand="document.getElementById('cmd_openhelp').removeAttribute('disabled');"/>
</window>

我们可以通过setAttribute和removeAttribute这两个方法,来设定某个command元素的disable和 enable,可以看到,如果disable了cmd_openhelp这个command,和它关联的按钮也自动变成灰色不可点击的状态:
Firefox扩展开发26