2008.04_XUL – część III_[Programowanie].pdf

(766 KB) Pobierz
439032486 UNPDF
Rozwiązania
XUL – wieloplatformowy język opisu interfejsu użytkownika – część III
XUL – część III
Grzegorz Madajczak
W pierwszej części minicyklu artykułów poświęconych XUL poznaliśmy poszczególne komponenty
służące do budowy graicznego interfejsu użytkownika. Drugi artykuł opisywał zagadnienia integracji
poszczególnych elementów oraz zaznajomił nas z podstawami użycia JavaScript do obsługi zdarzeń w
XUL. W ostatnim artykule z tego cyklu dowiemy się więcej na temat łączenia XUL z innymi odmianami
języka XML oraz zmiany wyglądu interfejsu z użyciem kaskadowych arkuszy stylu.
sposób można połączyć XUL z innymi
odmianami języka XML. Zapoznamy
się pokrótce z mechanizmem XPCOM ,
znacznie rozszerzającym możliwości tandemu XUL + Ja-
vaScript, a na koniec zajmiemy się również wprowadza-
niem zmian do wyglądu interfejsu, tak aby wyglądał on
inaczej niż standardowy, nieco siermiężny styl chrome .
Wszystkiego tego dowiemy się na przykładzie prostej
aplikacji – galerii do przeglądania plików graicznych.
Kod tej aplikacji będzie zaprezentowany na kolejnych li-
stingach i w miarę potrzeby omówiony, natomiast pliki
źródłowe znajdują się na stronie www.lpmagazine.org w
katalogu XUL / przyklad .
nazw w głównym elemencie < window > aplikacji. Zwią-
zane jest to z użyciem trzech różnych odmian języka
XML, dla których musimy zdeiniować zakres przestrze-
ni nazw – dla każdego z osobna. Całkowitą nowością jest
natomiast fragment kodu znajdujący się pomiędzy znacz-
nikiem otwierającym i zamykającym < svg:svg >. O jego
funkcji napiszę za chwilę. Do stworzenia aplikacji wy-
korzystano najnowszą specyikację języka XUL, zaim-
plementowaną w przeglądarce Firefox - 3.0 , która na razie
jest w fazie rozwojowej. Wersja ta wprowadza między
innymi kontrolkę < scale >, która tworzy suwak.
Omawiana aplikacja jest prostą przeglądarką ob-
razków. Uruchomienie aplikacji otwiera okno dialogo-
we, w którym należy wybrać folder z plikami graiczny-
mi. Obrazki z folderu zostaną wstawione jako elementy
listy w lewym panelu aplikacji. Interfejs graiczny skła-
da się z paska narzędzi (< toolbox >) oraz z dwóch paneli
z rozdzielaczem (< splitter >). Wszystkie te elementy zo-
stały omówione w poprzednich częściach minicyklu po-
święconego XUL. Na pasku narzędzi znajdują się przy-
ciski (< toolbarbutton >), obsługujące poszczególne funk-
cje związane z działaniem aplikacji – otwieraniem ob-
Opis aplikacji
Kod interfejsu aplikacji, widocznej na Rysunku 1.,
przedstawia Listing 1. Kod biblioteki JavaScript z funk-
cjami obsługującymi interfejs przedstawia Listing 2.
Przeglądając kod XUL z Listingu 1., większość elemen-
tów powinna wyglądać znajomo, poza niewielkimi no-
winkami. Jedną z nich jest deklaracja kilku przestrzeni
50
kwiecień 2008
W obecnym artykule zobaczymy, w jaki
439032486.020.png 439032486.021.png 439032486.022.png
 
Rozwiązania
XUL – wieloplatformowy język opisu interfejsu użytkownika – część III
Listing 1. Kod graicznego interfejsu użytkownika aplikacji Galeria napisany w języku XUL
<?xml version="1.0" encoding="iso-8859-2"?>
orient= "horizontal"
onchange= "magnify()" / >
< label id= "scaleValue" value= "100" / >
< label value= "%" / >
< toolbarseparator / >
< ?xml-stylesheet href= "gallery.css" type= "text/css" ? >
< window
id= "main"
title= "Galeria obrazków"
xmlns= "http://www.mozilla.org/keymaster/gatekeeper/
there.is.only.xul"
xmlns:svg= "http://www.w3.org/2000/svg"
xmlns:xlink= "http://www.w3.org/1999/xlink" >
< toolbarbutton image= "img/help.png" onclick
= "window.open('help.xul')" / >
< /hbox >
< /toolbar >
< /toolbox >
< script type= "text/javascript" src= "library.js" / >
< toolbox >
< toolbar >
< hbox lex= "1" class= "tb" align= "center" >
< hbox lex= "1" >
< listbox id= "lista" onclick= "setImage()" / >
< splitter
id= "identiier"
state= "open"
collapse= "before"
resizebefore= "closest"
resizeafter= "closest" >
< grippy / >
< /splitter >
< toolbarbutton image= "img/ileopen.png"
oncommand= "openImage()" / >
< toolbarbutton image= "img/previous.png"
oncommand= "nextprev(-1)" / >
< toolbarbutton image= "img/next.png"
oncommand= "nextprev(1)" / >
< vbox >
< svg:svg width= "600" height= "450" >
<toolbarbutton image="img/left.png"
oncommand="move('left')" />
< svg:g id= "moveEl" >
< svg:g id= "rotateEl" >
< svg:image
id= "svgimg"
xlink:href= ""
width= "600"
height= "450" / >
< /svg:g >
< /svg:g >
< /svg:svg >
< /vbox >
< /hbox >
< toolbarbutton image= "img/up.png"
oncommand= "move('up')" / >
< toolbarbutton image= "img/down.png"
oncommand= "move('down')" / >
< toolbarbutton image= "img/right.png"
oncommand= "mov('right')" / >
< toolbarbutton image= "img/redo.png"
onclick= "rotate('left')" / >
< toolbarbutton image= "img/undo.png" onclick= "r
otate('right')" / >
< script type= "text/javascript" >
< ![CDATA[
loaddata();
var mirrorVal = new Array(1,1);
var pozX = 0;
var pozY = 0;
var rotation = 0;
var imgWidth = parseInt(document.getElementById
( "svgimg" ).getAttribute( "width" ));
var imgHeight = parseInt(document.getElementByI
d( "svgimg" ).getAttribute( "height" ));
var idx = 0;
]] >
< /script >
< toolbarbutton image= "img/odbij.png" onclick
= "mirror('poziom')" / >
<toolbarbutton image="img/odbij_poziom.png"
onclick="mirror('pion')"/>
< label value= "Powiększenie: " / >
< scale id= "magnifyScale"
min= "10"
max= "100"
increment= "10"
value= "100"
</window>
www.lpmagazine.org
51
439032486.001.png 439032486.002.png 439032486.003.png 439032486.004.png 439032486.005.png 439032486.006.png
 
Rozwiązania
XUL – wieloplatformowy język opisu interfejsu użytkownika – część III
razków, poruszaniem się na liście obrazków
(lewy panel) oraz przekształceniami po-
większonego obrazka wybranego z listy.
domyślną, jak w przykładzie powyżej. No-
wa domyślna przestrzeń nazw będzie obo-
wiązywała do zamknięcia znacznika, który
ją zdeiniował. Kod pomiędzy znacznikiem
otwierającym, a zamykającym interpreto-
wany będzie na podstawie nowo zdeinio-
wanej przestrzeni nazw.
W omawianej aplikacji SVG, jak już
wspomniałem, służy do przekształceń grai-
ki rastrowej (obrazka) wyświetlanej w głów-
nym oknie programu. Funkcje przekształca-
jące zawarte są w bibliotece JavaScript. Nie
jest to odpowiednie miejsce na omawianie
języka SVG, lecz nie sposób wspomnieć o
mechanizmach zastosowanych w aplika-
cji. Więcej informacji na temat SVG można
znaleźć w artykule SVG do tworzenia stron
internetowych , który ukazał się na łamach
magazynu Linux+ w kwietniu 2007 roku (nr
4/2007 (120)). Na potrzeby obecnego arty-
kułu omówię jedynie zastosowane w przy-
kładowej aplikacji funkcje.
Pierwsza z funkcji przekształcających
obrazek zaimplementowana jest w funkcji
JavaScript o nazwie magnify() . Funkcja ta,
korzystając z modelu DOM wyszukuje ele-
ment o żądanym ID – w tym wypadku ele-
ment image będący znacznikiem wstawiają-
cym obrazek do dokumentu. Funkcja ma-
gnify() wstawia do tego elementu atrybut
transform - odpowiedzialny za przekształce-
nia obiektów w SVG. W tym przypadku wy-
konujemy przekształcenie na podstawie ma-
cierzy. Więcej na ten temat można znaleźć
w dokumentacji poświęconej przekształ-
ceniom w SVG na stronach W3C: http:
//www.w3.org/TR/SVG/coords.html#Trans-
formAttribute . Przekształcenia obiektu SVG
na podstawie macierzy wykorzystuje rów-
nież inna funkcja: miror() - służąca do od-
bijania obrazka w poziomie i w pionie. Po-
zostałe dwie funkcje – rotate() - obraca-
jąca obrazek o 90° - oraz move() przesuwa-
jąca obrazek w pionie i w poziomie wyko-
rzystują również podstawianie wartości do
atrybutu transform - wykorzystując odpo-
wiednie funkcje zaimplementowane w ję-
zyku SVG.
Powyższe zastosowanie SVG w XUL to
w sumie ambitne zadanie. Nie zaprzeczam,
że opanowanie przekształceń w SVG na po-
ziomie kodu wymaga nieco wiedzy. Zdecy-
dowanie prostszym przykładem jest zasto-
sowanie SVG do tworzenia elementów gra-
ficznych, np. ikon na przyciskach. Takie za-
stosowanie SVG prezentuje kod na Listin-
gu 3. W przykładzie tym przycisk o id svg-
button wykorzystuje możliwość wstawiania
obiektu pomiędzy znaczniki otwierający i
zamykający elementu button w XUL. W
tej konkretnej sytuacji wstawionym obiek-
tem jest kod SVG przedstawiający ikonę
– trzy nakładające się na siebie koła. Do-
łożenie do tego prostego skryptu zmienia-
jącego kolory kół tworzy bardzo efektow-
ny przycisk. Utworzenie takiego przyci-
sku stosując zwykła grafikę rastrową by-
łoby również możliwe, lecz zdecydowanie
mniej efektowne.
Wydawać by się mogło, że utworzenie ta-
kiego przycisku, zawierającego graikę SVG,
jest bardzo trudne – wymagające wiedzy na
temat SVG. Okazuje się jednak, że efektowne
elementy graiczne SVG możemy umieszczać
w aplikacji XUL posługując się dowolnym
edytorem graiki SVG, np. programem Ink-
scape . W tym celu należy stworzyć graikę,
posługując się szerokim wachlarzem narzę-
dzi dostarczanych przez program. Na koniec
graikę zapisujemy w czystym formacie SVG.
Teraz pozostało nam jedynie otworzenie pli-
ku *.svg w edytorze tekstowym, skopiowanie
kodu znajdującego się pomiędzy znacznikiem
otwierającym i zamykającym < svg > i wkleje-
nie go w wybrane miejsce dokumentu XUL,
np. pomiędzy znacznik otwierający i zmyka-
jący < button >. Ostatecznie modyikujemy de-
klarację przestrzeni nazw – i gotowe...
Łatwo zauważyć, że JavaScript nie roz-
różnia, który z elementów jest elementem
kodu SVG, a który elementem kodu XUL.
Dla JavaScript wszystkie one są elementa-
mi, które mają swoje ściśle określone miej-
sce w modelu dokumentu. Podobnie bę-
dzie z każdym innym językiem pochodnym
XML, który wprowadzimy do XUL.
XUL i SVG
– w jednym stali DOMu
Ciekawostką aplikacji jest zastosowanie ję-
zyka SVG, jako silnika do przekształceń
obrazka. SVG – język skalowalnej graiki
wektorowej, udostępnia bowiem wiele funk-
cji pozwalających na przekształcanie grai-
ki rastrowej. Funkcje te nie są tak rozbudo-
wane, jak w przypadku profesjonalnych pro-
gramów graicznych, lecz i tak oferują du-
żo więcej, niżby można było się spodziewać
po aplikacji napisanej z użyciem dostępnych
nam metod.
Zastosowanie SVG we wnętrzu kodu
XUL jest dobrym przykładem na możliwość
mieszania różnych odmian XML w jednym
dokumencie. W omawianym przypadku SVG
znalazło się we wnętrzu XUL. Stosując taką
mieszankę należy pamiętać o zdeiniowaniu
przestrzeni nazw języków stosowanych w da-
nym dokumencie, a następnie odwoływanie
się do nich przy użyciu przedrostków, np.
<svg:svg width="600" height="450"
xmlns="http://www.w3.org/2000/svg">
<!-- Kod SVG -->
</svg:svg>
Aby uniknąć konieczności ciągłego stoso-
wania przedrostka przestrzeni nazw, moż-
na zdeiniować ją ponownie w elemencie
nadrzędnym dla danego kodu i uczynić ją
Rysunek 1. Przykładowa aplikacja napisana w XUL, omawiana w bieżącym artykule
52
kwiecień 2008
439032486.007.png 439032486.008.png 439032486.009.png
 
Rozwiązania
XUL – wieloplatformowy język opisu interfejsu użytkownika – część III
Listing 2. Kod biblioteki funkcji JavaScript obsługujących GUI aplikacji.
function getFiles()
{
var fp = Components.classes["@mozilla.org / ilepicker ; 1
"].createIns tan ce(nsIFilePicker);
fp.appendFilters(nsIFilePicker.ilterImages) ;
fp.init(window, "Select a File ",
nsIFilePicker.modeOpen);
var res = fp.show() ;
if (res == nsIFilePicker.returnOK)
{
var path = fp.ile.path ;
var listaElement = document.getElementById( "lista" ) ;
var newItemEl = document.createElement( "listitem
" ) ;
var img = document.createElement("image ");
img.setAttribute( "src" , path) ;
img.setAttribute( "width" , " 200 ");
img.setAttribute( "height" , " 150 ");
img.setAttribute(" onclick "," setImage() ; ");
newItemEl.appendChild(img) ;
listaElement.appendChild(newItemEl) ;
var image = document.getElementById( "svgimg" ) ;
image.setAttribute( "xlink:href" , path) ;
}
}
try
{
netscape.security.PrivilegeManager.enablePrivilege( "Uni
versalXPConnect" ) ;
}
catch (e)
{
alert( "Brak uprawnien do odczytu / zapisu
lokalnych plikow" ) ;
}
var startPath ;
var nsIFilePicker = Components.interfaces.nsIFilePic
ker ;
var fp = Components.classes["@mozilla.org / ilepicker ; 1
"].createIns tan ce(nsIFilePicker);
fp.init(window, "Select a File ", nsIFilePicker.modeG
etFolder);
var res = fp.show()
if (res == nsIFilePicker.returnOK)
{
startPath = fp.ile.path ;
}
var ile = Components.classes["@mozilla.org / ile / local ;
1 "].createIns tan ce(Components.interfaces.nsILocalFile);
function loaddata()
{
var photos = getFiles() ;
var listaElement = document.getElementById( "lista" ) ;
ile.initWithPath(startPath) ;
var entries = ile.directoryEntries ;
var Farray = [] ;
while (entries.hasMoreElements())
{
var entry = entries.getNext() ;
entry.QueryInterface(Components.interfaces.nsIFi
le) ;
Farray.push(entry.path) ;
}
return Farray ;
}
for (i = 0 ; i < photos.length ; i++)
{
var newItemEl = document.createElement( "listitem
" ) ;
var img = document.createElement("image ");
img.setAttribute( "src" , photos[i]) ;
img.setAttribute( "width" , " 200 ");
img.setAttribute( "height" , " 150 ");
img.setAttribute(" onclick "," setImage() ; ");
newItemEl.appendChild(img) ;
listaElement.appendChild(newItemEl) ;
}
}
function openImage()
{
try
{
netscape.security.PrivilegeManager.enablePrivilege
( "UniversalXPConnect" ) ;
}
catch (e)
{
alert( "Brak uprawnien do odczytu / zapisu
lokalnych plikow" ) ;
}
var path ;
var nsIFilePicker = Components.interfaces.nsIFilePic
ker ;
function setImage()
{
var lista = document.getElementById( "lista" ) ;
var image = document.getElementById( "svgimg" ) ;
var imageSrc = lista.childNodes[lista.selectedIndex].
childNodes[ 0 ].getAttribute( "src" ) ;
image.setAttribute( "xlink:href" , imageSrc) ;
idx = lista.selectedIndex ;
}
function magnify()
{
var image = document.getElementById( "svgimg" ) ;
var scale = document.getElementById( "magnifyScale" ) ;
www.lpmagazine.org
53
439032486.010.png 439032486.011.png 439032486.012.png 439032486.013.png 439032486.014.png 439032486.015.png
 
Rozwiązania
XUL – wieloplatformowy język opisu interfejsu użytkownika – część III
Listing 2b. Kod biblioteki funkcji JavaScript obsługujących GUI aplikacji
XML w XUL
Wielokrotnie podkreślałem, że jedną z na-
czelnych cech języków pochodnych od
XML, jest możliwość wzajemnego prze-
platania się w jednym dokumencie. W po-
przednim rozdziale artykułu omawiałem
sytuację, gdy SVG jest wtrącone do kodu
XML. Przeglądając kod na Listingu 3. moż-
na zobaczyć, że znajduje się tam fragment
kodu w XHTML tworzący tabelę. Z równą
łatwością można stosować każdy inny ele-
ment języka XHTML, jak i każdy inny język
pochodny XML, np. MathML , czy XForms
(więcej o tych językach m. in. na stronie
http://www.w3schools.com ).
Możliwa jest również sytuacja odwrot-
na, kiedy to kod XUL znajduje się we-
wnątrz innego języka XML. Takie rozwią-
zanie można odnaleźć w dokumencie ne-
sted_xul.xhtml , który znajduje się w folde-
rze XUL / przyklad na płycie dołączonej do
czasopisma. Przy takim rozwiązaniu należy
jednak liczyć się z tym, że nie wszystkie ele-
menty XUL będą prawidłowo działały.
var scaleLabel = document.getElementById( "scaleValue" ) ;
var imageGr = document.getElementById( "moveEl" ) ;
scaleLabel.setAttribute( "value" , scale.value) ;
var sxy = scale.value / 100 ;
imgWidth = 600 * scale.value / 100 ;
imgHeight = 450 * scale.value / 100 ;
pozX = ( 600 – imgWidth) / 2 ;
pozY = ( 450 – imgHeight) / 2 ;
imageGr.setAttribute( "transform" , "matrix(" + sxy + ", 0 , 0 , " + sxy + " , " +
pozX + " , " + pozY + " ) ");
}
function rotate(dir)
{
var image = document.getElementById( "rotateEl" ) ;
if (dir == "right" ) rotation = rotation + 90 ;
else rotation = rotation – 90 ;
image.setAttribute( "transform" , "rotate(" + rotation
+ ")" ) ;
}
function mirror(dir)
{
var image = document.getElementById( "rotateEl" ) ;
var imageMoveEl = document.getElementById( "moveEl" ) ;
XPCOM
XPCOM (z ang. Cross Platform Compo-
nent Object Model ) to międzyplatformowy
model komponentów podobnym do mecha-
nizmu CORBA znanego z Gnome lub COM
irmy Microsoft . W przykładowej aplikacji
mechanizm XPCOM został wykorzystany
do otwierania folderu zawierającego grai-
kę oraz otwieranie pojedynczych plików
graicznych.
Mechanizm otwierający folder z plika-
mi graicznymi znajduje się w funkcji get-
Files() (Listing 2.). Pierwsze wiersze ko-
du funkcji stanowią kluczowy element całej
aplikacji. Funkcja
if (dir == "poziom" )
{
mirrorVal[ 0 ] = mirrorVal[ 0 ] * - 1 ;
pozX = pozX - (imgWidth + 20 ) * mirrorVal[ 0 ] ;
}
if (dir == "pion" ) {
mirrorVal[ 1 ] = mirrorVal[ 1 ] * - 1 ;
pozY = pozY - (imgHeight + 20 ) * mirrorVal[ 1 ] ;
}
image.setAttribute( "transform" , "matrix(" + mirrorVal[ 0 ] + ", 0 , 0 ," +
mirrorVal[ 1 ] + "," + pozX + "," + pozY + ")" ) ;
}
function move(dir) {
var imageMoveEl = document.getElementById( "svgimg" ) ;
if (dir == "up" ) pozY = pozY – 10 ;
if (dir == "down" ) pozY = pozY + 10 ;
if (dir == "right" ) pozX = pozX + 10 ;
if (dir == "left" ) pozX = pozX – 10 ;
imageMoveEl.setAttribute( "transform" , "translate(" + pozX + "," + pozY +
")" ) ;
}
function nextprev(i) {
var image = document.getElementById( "svgimg" ) ;
var lista = document.getElementById( "lista" ) ;
idx = idx + i ;
var imageSrc = lista.childNodes[idx].childNodes[ 0 ].getAttribute( "src" ) ;
image.setAttribute( "xlink:href" , imageSrc) ;
}
netscape.security.PrivilegeManager
.enablePrivilege("UniversalXPConne
ct");
włącza bowiem możliwość odczytu i zapisu
plików przez JavaScript za pośrednictwem
przeglądarki internetowej. Jeśli wcześniej
możliwość taka została zablokowana, poja-
wi się stosowny komunikat o braku upraw-
nień do powyższych czynności.
Pierwszym etapem wykorzystującym
mechanizm XPCOM jest utworzenie nowej
instancji komponentu nsILocalFile , obsłu-
gującego operacje wejścia/wyjścia strumie-
nia z/do pliku.
var nsIFilePicker = Components.inter
faces.nsIFilePicker;
54
kwiecień 2008
439032486.016.png 439032486.017.png 439032486.018.png 439032486.019.png
 
Zgłoś jeśli naruszono regulamin