Definition af menuer og værktøjslinjer i XML

Indledning

Mens handlingsmønstret tillader at handlinger som aktiveres af brugeren kapsles ind i et objekt, som kan "forbindes" til et sted i menulinjerne eller værktøjslinjerne, løser det ikke i sig selv problemet med at oprette selve menuerne. I særdeleshed skal du bygge alle sammenhængsafhængige menuer i C++ kode, og udtrykkelig indsætte handlingerne i en vis rækkefølge, med hensyn taget til stilguiden for standardhandlinger. Det gør det rigtigt svært at lade brugeren indstille menuerne eller ændre genvejstaster så de passer til hans behov, uden at ændre kildekoden.

Dette problem løses med en samling klasser som kaldes grafisk XML-grænseflade. I grunden adskiller de handlingerne (kodede i C++) fra deres udseende i menulinjer og værktøjslinjer (kodede i XML). Uden at ændre nogen kildekode, kan menuer nemt indstilles ved at justere en XML-fil. Desuden hjælper det til at sikre at standardhandlinger (såsom Fil->Åbn eller Hjælp->Om) vises på de steder som foreslås af stilguiden. Grafiske XML-grænseflader er særligt vigtige for modulære programmer, hvor valgmulighederne i menulinjerne kan komme fra mange forskellige indstiksprogrammer eller dele.

KDE's klasse for topniveauvindue, KMainWindow, arver KXMLGUIClient, og understøtter derfor grafiske XML-grænseflader fra begyndelsen. Alle handlinger som laves inde i det skal have klientens actionCollection() som forælder. Et kald til createGUI() bygger siden hele sættet af menuer og værktøjslinjer som defineres af programmets XML-fil (almindeligvis med endelsen ui.rc).

Et eksempel: Menuen i Kview

I det følgende bruger vi KDE's billedfremviser Kview som eksempel. Den har en ui.rc-fil som hedder kviewui.rc, som installeres med et fragment fra Makefile.am

rcdir = $(kde_datadir)/kview
rc_DATA = kviewui.rc

Her er et uddrag fra filen kviewui.rc. For enkelhedens skyld, viser vi kun definitionen for menuen View.

<!DOCTYPE kpartgui>
<kpartgui name="kview">
  <MenuBar>
    <Menu name="view" >
      <Action name="zoom50" />
      <Action name="zoom100" />
      <Action name="zoom200" />
      <Action name="zoomMaxpect" />
      <Separator/>
      <Action name="fullscreen" />
    </Menu>
  </MenuBar>
</kpartgui>

Den tilsvarende del til at oprette dette i C++ er:

KStdAction::zoomIn    ( this, SLOT(slotZoomIn()), actionCollection() );
  KStdAction::zoomOut   ( this, SLOT(slotZoomOut()), actionCollection() );
  KStdAction::zoom      ( this, SLOT(slotZoom()), actionCollection() );
  new KAction           ( i18n("&Half size"), ALT+Key_0, 
                          this, SLOT(slotHalfSize()), 
                          actionCollection(), "zoom50" );
  new KAction           ( i18n("&Normal size"), ALT+Key_1,
                          this, SLOT(slotDoubleSize()), 
                          actionCollection(), "zoom100" );
  new KAction           ( i18n("&Double size"), ALT+Key_2, 
                          this, SLOT(slotDoubleSize()), 
                          actionCollection(), "zoom200" );
  new KAction           ( i18n("&Fill Screen"), ALT+Key_3, 
                          this, SLOT(slotFillScreen()), 
                          actionCollection(), "zoomMaxpect" );
  new KAction           ( i18n("Fullscreen &Mode"), CTRL+SHIFT+Key_F, 
                          this, SLOT(slotFullScreen()), 
                          actionCollection(), "fullscreen" );

Menuen View som laves af denne definition af den grafiske grænseflade ser ud som det vises på dette skærmaftryk:



XML-filen begynder med en dokumenttypedeklaration. DTD'en for kpartgui findes i kdelibs-kildekoden i kdeui/kpartgui.dtd. Det yderste element i filen indeholder programmets instansnavn som en egenskab. Det kan også indeholde et versionsnummer på formen "version=2". Dette er nyttigt når du udgiver nye versioner af et program med ændret menustruktur, f.eks. med flere funktioner. Hvis du hæver versionsnummeret i filen ui.rc, sørger KDE for at alle indstillede versioner af filen kasseres og at den nye fil bruges i stedet.

Næste linje, <MenuBar>, indeholder en deklaration af en menulinje. Du kan også indsætte så mange <ToolBar>-deklarationer som helst, for at oprette nogle værktøjslinjer. Menuen indeholder en undermenu, med navnet "view". Dette navn er allerede fordefineret, og derfor vil den oversatte version af ordet "View" blive vist. Hvis du deklarerer undermenuer, skal du udtrykkelig tilføje titlen. Kview har for eksempel en undermenu med titlen "Image", som deklareres som følger:

<Menu name="image" >
   <text>&amp;Image</text>
   ...
</Menu>

I KDE's automatiske byggeskelet, plukkes sådanne titler automatisk ud og placeres i programmets .po-fil, så de håndteres af oversættere. Bemærk at du skal skrive markeringen af genvejstasten "&" på en form som følger XML-syntaksen "&amp;".

Lad os vende tilbage til eksemplet. Kview's menu Vis indeholder et antal egne handlinger zoom50, zoom100, zoom200, zoomMaxpect og fullscreen, deklarerede med elementet <Action>. Skillelinjen i skærmaftrykkene svarer til elementet <Separator>.

Du bemærker at visse menupunkter ikke har et tilsvarende element i XML-filen. De er standardhandlinger. Standardhandlinger laves af klassen KStdAction. Når du laver sådanne handlinger i dit program (som i C++ eksemplet ovenfor), indsættes de automatisk på en foreskreven plads, og muligvis med en ikon og en genvejstast. Du kan slå disse steder i filen op i kdeui/ui_standards.rc i kdelibs-kildekoden.

Et eksempel: Værktøjslinjer i Konqueror

For beskrivelsen af værktøjslinjer, skifter vi til Konquerors definition af grafisk grænseflade. Dette uddrag definerer stedlinjen, som indeholder indtastningsfeltet for URL'er.

<ToolBar name="locationToolBar" fullWidth="true" newline="true" >
  <text>Location Toolbar</text>
  <Action name="clear_location" />
  <Action name="location_label" />
  <Action name="toolbar_url_combo" />
  <Action name="go_url" />
</ToolBar>

Det første vi bemærker er at der er mange flere egenskaber end for menulinjer. De omfatter:

  • fullWidth: Fortæller den grafiske XML-grænsefladen at værktøjslinjen har samme bredde som topniveauvinduet. Hvis dette er "false", optager værktøjslinjen kun så meget plads som nødvendigt, og yderligere værktøjslinjer placeres på samme linje.

  • newline: Dette hører sammen med ovenstående valgmulighed. Hvis newline er "true", så placeres værktøjslinjen på en ny linje. Ellers kan den placeres i en linje sammen med den foregående værktøjslinje.

  • noEdit: Normalt kan værktøjslinjer indstilles af brugeren, f.eks. med Indstillinger->Indstil værktøjslinjer i Konqueror. Sættes dette til "true", markeres værktøjslinjen så den ikke kan redigeres. Det er vigtigt for værktøjslinjer som fyldes op med objekter når programmet kører, f.eks. Konqueror's bogmærkeværktøjslinje.

  • iconText: Beder den grafiske XML-grænseflade om at vise handlingens tekst før ikonen. Normalt vises teksten kun som et værktøjsvink når musemarkøren holdes over ikonen et stykke tid. Mulige værdier for egenskaben er "icononly" (viser kun ikonen), "textonly" (viser kun teksten), "icontextright" (viser teksten til højre for ikonen) og "icontextbottom" (viser teksten under ikonen).

  • hidden: Hvis dette er "true", så vises værktøjslinjen ikke fra begyndelsen, og skal aktiveres af et menupunkt.

  • position: Standardværdien for denne egenskab er "top", hvilket betyder at værktøjslinjen placeres under menulinjen. For programmer med mange værktøjer, såsom grafikprogram, kan det være interessant at erstatte dette med "left" (venstre), "right" (højre) eller "bottom" (under).

Dynamiske menuer

XML kan naturligvis kun indeholde en statisk beskrivelse af en brugergrænseflade. Ofte er der menuer som ændres under kørsel. Konqueror's menu Sted indeholder for eksempel et sæt punkter Åbn med ..., med programmer som kan indlæse en fil med en given Mime-type. Hver gang dokumentet som vises ændres, opdateres listen med menupunkter. Den grafiske XML-grænseflade er forberedt på at håndtere sådanne tilfælde med begrebet handlingslister. En handlingsliste deklareres som et objekt i XML-filen, men består af flere handlinger som forbindes til menuen når programmet kører. Ovenstående eksempel implementeres med følgende deklaration i Konqueror's XML-fil:

<Menu name="file">
  <text>&amp;Location</text>
  ...
  <ActionList name="openwith">
  ...
</Menu>

Funktionen KXMLGUIClient::plugActionList() bruges derefter for at tilføje handlinger som skal vises, mens funktionen KXMLGuiClient::unplugActionList() fjerner alle forbundne handlinger. Rutinen som er ansvarlig for at udføre opdateringerne ser ud som følger:

void MainWindow::updateOpenWithActions()
{
    unplugActionList("openwith");
    openWithActions.clear();
    for ( /* Løkke for relevante tjenester */ ) {
        KAction *action = new KAction( ...);
        openWithActions.append(action);
    }
    plugActionList("openwith", openWithActions);
}

Bemærk at i modsætning til statiske handlinger, så laves disse ikke med handlingssamlingen som forælder, og du har selv ansvaret for at de fjernes. Den nemmeste måde at opnå dette er ved at bruge openWithActions.setAutoDelete(true) i eksemplet ovenfor.

Sammenhængsafhængige menuer

Eksemplerne ovenfor indeholder kun klasser hvor et hovedvindues menulinje og værktøjslinjer laves. I de tilfælde er processen som laver beholdarne helt skjult for dig inde i kaldet af funktionen createGUI() (medmindre du har egne beholdere). Der er dog tilfælde hvor du vil oprette andre beholdere og befolke dem med grafiske grænsefladedefinitioner fra XML-filen. Et sådant eksempel er sammenhængsafhængige menuer. For at få en peger til en sammenhængsafhængig menu, skal du bede klientens tilvirker om det:

void MainWindow::popupRequested()
{
    QWidget *w = factory()->container("context_popup", this);
    QPopupMenu *popup = static_cast<QPopupMenu *>(w);
    popup->exec(QCursor::pos());
}

Metoden KXMLGUIFactory::container() som bruges ovenfor, ser efter om den finder en beholder i XML-filen med det angivne navn. Altså kan en mulig definition se ud på følgende måde:

...
<Menu name="context_popup">
  <Action name="file_add"/>
  <Action name="file_remove"/>
</Menu>
...