級別: 中級
David Mertz, Browser, Gnosis Software, Inc.
2004 年 5 月 26 日
David
繼續關于如何在 GUI 界面配置中使用 XML 的話題。他考察了 Mozilla 基于 XML
的用戶界面語言(XUL),這種語言編寫的應用程序完全不依賴所選擇的底層操作系統。初看起來這似乎有點奇怪,但很快您就會看到該 Mozilla
項目為構建 GUI 提供了非常強大的工具,允許您開發范圍廣泛的用戶界面。
在開始討論那些并非所有
讀者都熟悉的縮寫字符串和奇怪的名詞之前,我首先簡要地介紹一些基礎知識。簡單地講,Mozilla 所要做的就是呈現 XML 和
HTML——如此而已。在 Mozilla 中,您習以為常的所有 Web 瀏覽器控件(后退按鈕、URL
文本框、書簽、菜單等等)以及任何內容區域,都只是一些 XUL(XML-based User Interface Language,基于 XML
的用戶界面語言)或者 HTML 數據的呈現。閱讀新聞、郵件、聊天記錄或編輯 HTML 的 Mozilla 應用程序同樣如此——全部都只是呈現
XML。Mozilla 在其默認應用程序中提供了一組 chrome(非文檔部件/控件),但是該框架允許通過編寫少量的 XUL XML 創建您自己的應用程序,或者擴展 Mozilla 提供的應用程序。
整
個 Mozilla 應用程序包和相關的附件(如 Firefox 和 Thunderbird),其核心就是用 XUL 編寫的,XUL
是一種用于定義用戶界面的 XML 語言。整個 Mozilla 項目劃分成幾層。最底層是 Gecko Runtime
Engine,它負責在屏幕上呈現可視化元素,即 HTML 標簽和相關的屬性、子元素以及 URL 引用的資源。很多 Mozilla 應用程序使用
XPToolkit 作為下一層,就是這一層上支持 XUL;但是一些應用程序喜歡使用 Mac OS X 上的 Camino
瀏覽器代替本地部件,作為 Gecko 引擎的界面。
當然如果沒有程序邏輯,應用程序就做不了多少工作。只需要使用 JavaScript
處理程序邏輯,就可以編寫完整的 Mozilla 應用程序——每個 XUL 控件激活一些配置好的 JavaScript
函數,可能還需要根據那個控件傳遞參數。(像文本字段、選擇列表或者滾動條這樣的控件還用于選擇特定的值,而不僅僅是觸發單個動作。)但是對于更高級的應
用程序,跨平臺組件對象模塊(
Cross Platform Component Object Module,XPCOM)接口提供了一種方式,讓 XUL
配置的界面調用其他編程語言編寫的組件——主要是使用 C++ 編寫的組件,但是其他編程語言如 Python 也提供了 XPCOM
綁定。XPCOM 類似于 Windows 中的 COM,或者 CORBA。
現在我已經描述了開發 Mozilla 界面的總體框架(XUL 調用
JavaScript,JavaScript 可以通過 XPCOM 調用 C++ 組件),本文后面將專門討論這一龐雜體系中和 XUL
有關的那一部分,畢竟這是一個關于 XML 的專欄。例子中調用的 JavaScript alert()
函數沒有多少意義,調用自定義功能的實用應用程序可以代之以其他函數。
一個小小的應用程序
為了讓您領略 XUL 代碼,我編寫了一個定制的 Mozilla 應用程序
SimpleApp
,它包括一個 XUL 文件
SimpleApp.xul
和一個外部 JavaScript 文件
SimpleApp.js
。這個應用程序僅僅在屏幕上顯示幾個菜單和工具欄按鈕,剩下的空間顯示 HTML 文檔。
SimpleApp
中配置的大部分動作僅僅是彈出一些警告框,但也有一個工具欄按鈕允許選擇要顯示的 HTML 文檔 URL。在 HTML 區域中可以像在普通瀏覽器中那樣導航:單擊鏈接、填寫表單以及完成其他操作。
首先,看一看如何選擇 HTML URL:
圖 1. 在 SimpleApp 中選擇 URL
SimpleApp
使用了幾種不同類型的菜單。對于實際的應用程序這可能并非最好的設計,但是我想同時說明如何使用由
<menubar>
標簽創建的本地菜單,以及用
<menulist>
標簽創建的 XUL 菜單。后一種菜單可以出現在應用程序中的任何位置,包含在嵌套區域中,或者作為彈出菜單附加到可視化內容上。但是,XUL 也允許使用
<menupopup>
創建系統風格的彈出菜單,本例中沒有說明這種菜單(系統風格的菜單通過右擊鼠標或者按住 Shift 鍵單擊鼠標來激活,通常
<menulist>
附加在框架中顯示的文本上。
圖 2 說明如何從本地 Mac OS X 菜單激活警告框:
圖 2. SimpleApp 中的本地菜單
Foo
和
Bar
就像是帶有子項的普通下拉菜單,盡管在截屏圖像中沒有顯示出來。
觀察 XUL
現在來看一看組成
SimpleApp
的 XUL。像多數應用程序一樣,它的根元素是
<window>
。但是,僅用于增強已有應用程序如 Mozilla 瀏覽器功能的實用程序,根元素應該是
<overlay>
。一個應用程序可以包含多個覆蓋圖。
我把這個 XUL 文件分解成幾部分,每一部分都有明確的功能。如果需要可以把各部分合并起來。
清單 1. SimpleApp.xul 頭和腳本
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/xul.css" type="text/css"?> <!DOCTYPE window> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" width="600" height="280" title="SimpleApp"> <script src="SimpleApp.js" /> <script type="application/x-javascript"> <![CDATA[ function say(txt) { alert(txt); } ]]> </script> <script type="application/x-javascript"> <![CDATA[ function newcontent(url) { frames['content'].location.href=url; } ]]> </script>
|
外部文件
SimpleApp.js
僅僅定義了一個簡單的 JavaScript 函數
eye()
,它被那個帶有眼睛圖標的工具欄按鈕激活。在實際的應用程序中,很可能要把必要的 JavaScript 函數庫放在這類外部文件中。我還在 XUL 文件的正文部分定義了兩個簡單的 JavaScript 函數。
清單 2. SimpleApp.xul 系統菜單配置
<menubar id="main-menu"> <menu label="File"> <menupopup> <menuitem label="Hello" oncommand="say('Hello world!');"/> </menupopup> </menu> </menubar>
|
在清單 2 中,
SimpleApp
定義的
File
菜單包含一個子菜單項
Hello
。
圖 2顯示了激活該菜單時的情景。
現在看一看窗口內容是如何安排的:
清單 3. SimpleApp.xul 布局框和菜單列表
<vbox> <hbox> <menulist><label>Foo</label> <menupopup> <menuitem label="New SimpleApp" oncommand="window.open('SimpleApp.xul','','chrome');"/> <menuitem label="Something Else" oncommand="window.open('test3.xul','','chrome');"/> </menupopup> </menulist> <menulist><label>Bar</label> <menupopup oncommand="alert('Menu item invoked.')"> <menuitem label="Item One"/> <menuitem label="Item Two"/> <menuitem label="Item Three"/> </menupopup> </menulist> </hbox>
|
控制 XUL 空間幾何形狀很有用的一種辦法是將其放在以下幾種不同的框中:
-
<vbox>
和
<hbox>
(常常一起使用)
-
<scrollbox>
-
<groupbox>
這里沒有說明
<grid>
元素的用法,它也可以取得類似的效果。
<vbox>
總是把其中的內容布置在垂直的框中(框本身是不可見的),類似地,
<hbox>
元素把內容布置在水平的框中。如果需要,可以增加
height
和
width
屬性明確規定這些容器的大小,也可以使用
flex
、
autosize
或
equalsize
在其他元素中(比如其他框)安排框的布局。
具體而言,
SimpleApp
中的控件都放在一個控制用的
<vbox>
中,其中一個子框
<hbox>
用于
Foo
和
Bar
菜單,另一個
<hbox>
放置工具欄:
清單 4. SimpleApp.xul 布局框和工具欄
<hbox> <toolbarbutton type="button" oncommand="alert('Button invoked.');"> <image src="http://gnosis.cx/publish/images/doc.gif" /> </toolbarbutton> <toolbarbutton type="menu"> <image src="http://gnosis.cx/publish/images/note.gif" /> <menupopup> <menuitem label="IBM" oncommand="newcontent('http://ibm.com/');"/> <menuitem label="Google" oncommand="newcontent('http://google.com/');"/> <menuitem label="TPiP" oncommand="newcontent('http://gnosis.cx/TPiP/');"/> </menupopup> </toolbarbutton> <toolbarbutton type="menu-button" onclick="eye();"> <image src="http://gnosis.cx/publish/images/eye.gif"/> </toolbarbutton> </hbox> </vbox>
|
工具欄代碼很有意思,Mozilla XUL 支持現代風格的工具欄,既有直接激活的按鈕,也有包含下拉子菜單的按鈕。貼有 note.gif 圖標的按鈕就是一個下拉菜單風格的按鈕,如
圖 1中所示。還要注意,工具欄中實際的圖像是從遠程 URL 中拉回來的。這個非常簡單的例子是一個完全網絡化的應用程序——作為一個整體結合了整個 Web 上的資源(雖然這個例子非常簡單)。
謎團中的最后一部分是顯示的 HTML 內容。為此,需要在
html
名稱空間中創建一個
<iframe>
:
清單 5. SimpleApp.xul HTML 內容
<html:iframe id="content-frame" name="content" src="http://gnosis.cx/TPiP/" flex="100%"/> </window>
|
SimpleApp
的布局比較合理,基本上類似于普通的瀏覽器。您還可以堆砌上更多的資源,得到更加華麗、更加復雜的布局。比如,在實驗 XUL 的時候我創建了一個測試案例,其中包含大小古怪的按鈕和各種不同的遠程 HTML 頁面。它揭示了更多的布局特性,不妨把它拷貝到您的系統中:
清單 6. 華麗的 XUL 試驗品(test.xul)
<?xml version="1.0"?> <grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <columns> <column/> <column/> </columns> <rows flex="1"> <row> <image src="http://www.mozilla.org/images/mozilla-banner.gif"/> </row> <row flex="1"> <spacer width="10"/> <vbox align="center"> <spacer height="10"/> <description style="border:0px; width: 120px"> This text can be <html:b> bold </html:b> and can wrap to several lines. If I add a few more words, I can get it to fill its column. This is a weird interface, eh? </description> <spacer height="10"/> <button accesskey="A" label="A" image="http://gnosis.cx/TPiP/hugo.gif" oncommand="alert('Button A pressed!');"/> </vbox> <spacer width="10"/> <iframe flex="1" src="http://google.com"/> </row> <row flex="1"> <iframe flex="1" src="http://ibm.com/" /> <button accesskey="B" flex="1" label="Button B" oncommand="alert('Button B pressed!');"/> <button accesskey="C" flex="1" label="Button C" oncommand="alert('Button C pressed!');"/> </row> </rows> </grid>
|
|
|
其他部件
這個示例應用程序只使用了少量的 XUL 控件。關于各種 XUL 元素的完備文檔請參閱“XUL
Programmer's Reference Manual”,在線書籍“
Creating XPCOM Components”也是非常不錯的參考資料,詳細介紹了 XUL 和 XPCOM 庫編程。
我們再簡單地介紹幾種 XUL 元素。網格和框多用于簡單的布局,而 deck、stack 和 bulletin board 可以提供更多的控制。后面的
<bulletinboard>
可以精確定位,而不是自動浮動控件以適應容器。
除了上述菜單之外,XUL 中還包括基本的 UI 元素,如單選框、檢查框和各種不同的按鈕。一些更高級的用戶界面控件還有樹狀圖、彈出窗口、編輯組件和選項卡,以及進度的顯示和帶有不同滾動條的大型容器的操作。
多數情況下,所有這些元素都按照其用途命名。但是在 XUL 應用程序中可以包括喜歡的任何 HTML 部件。只需要將其放在
html
名稱空間中并添加上,就像您在設計 DHTML 網頁時所做的那樣。
在
清單 6 中,我悄悄地引入了一種還沒有提到過的非常有用的導航元素。那些按鈕有一個屬性
accesskey
,可以使用鍵盤或者點擊鼠標激活它們的動作。同樣,也可以規定您認為什么樣的鍵盤導航最適合您的應用程序。雖然默認情況下,可以使用制表鍵和箭頭鍵選擇菜單項,但是快捷鍵可以簡化很多動作。
結束語
在
瀏覽器中開發完整的應用程序似乎很奇怪,但是現在 Mozilla 還不僅如此——它是一整套組件和 GUI 體系結構。事實上,Mozilla
和其他您所能夠想到的任何 GUI 庫相比,可能更富有跨平臺的特色,也更廣泛地安裝在用戶系統上。您所認為的通用
GUI/部件庫——Qt、wxWindows、GTK、FOX、MFC、.NET、Carbon
等等——各有不同的優缺點。但其中沒有一個可以認為已經跨用戶系統安裝。很多只能在 Mozilla
所支持平臺中的一部分上使用,而且多數安裝比較困難還存在許可的問題。僅僅因為它是一個非凡的瀏覽器就值得安裝
Mozilla;一旦擁有它,您就有了一個定制應用程序的 自由的平臺。
要使您的 Mozilla/XUL 應用程序完全跨平臺,必須用 XUL 配置 GUI,并用 JavaScript 編寫程序邏輯。這并不是討論 JavaScript 優劣的地方,但顯然有很多程序員更喜歡用其他語言開發,如 C++、Python 和 Perl。您
可以這
樣做,但是需要使用 XPCOM 綁定到外部庫,而這樣會降低可移植性,或者至少增加了移植的難度。對于 Python 和 Perl
這樣的腳本語言,您可以利用通用的綁定/包裝程序,編寫非常通用的代碼(但是仍然需要適應平臺的 XPCOM,如PyXPCOM)。但是對于
C++,您需要對希望支持的每種平臺編譯特定版本的庫,要做的工作更多一些。對于簡單的應用程序 JavaScript
仍然是最好的選擇,特別是如果應用程序很大程度上基于 Mozilla 框架中內在的網絡功能。
參考資料
關于作者