来自 : http://qnet.blog.35.cn/archives/65/
作者: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>[email protected]</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的例子:
由上图的地址栏可以看出,打开FireFTP这个扩展,实际上就是打开了fireftp的content类型文件中的fireftp.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>之间一样。
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属性为按钮上显示的文字。
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>
文本输入框,为<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"。
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>
<?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>中。
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>
至此,我们介绍了用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打开。
运行得不错,不过,“密码:”旁边的输入框似乎靠的太近了些,我们可以把两个文字标签、两个输入框,分别放在两个个<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>
当我们把上面的窗口拖大,窗口控件还是停留在窗口的左边,留下右边一大片空白,这似乎不是我们想要的效果:
我们可以在<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>
有的时候,窗口中一部分空间是相互关联的,为了表示出这种关联关系,要用到<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>
显示效果:
<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>
标签盒子
<?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>
<?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>表示工具栏按钮:
菜单栏
<?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>为分隔符。
前面几节,讨论了怎么用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中一样,弹出窗口显示:
通过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就是对象标签的名称,可以通过它识别事件实在哪个标签(也就 是控件)上被激活的。效果如下:
在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也找不到它。
上一篇我们看到了如何用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上移动,就会触发函数显示当前鼠标 的位置(鼠标没有被抓下来):
上一篇的第二个例子,我们用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>
键盘快捷键是一个应用程序不可缺少的部分,最常见的地方在菜单栏中,在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 |
当我们用鼠标点击一个控件,或者用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>
显示效果:
通过设置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 获得当前被激活的控件对象。
何为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,和它关联的按钮也自动变成灰色不可点击的状态: