MagnaMedia · AMIGA-Magazin · Intuition-Programmierung unter pOS, Folge 2

Aktuelles Heft 8/97

Intuitive Benutzeroberfl�chen

Wir besch�ftigen uns diesmal mit der Gadgetprogrammierung. Nach diesem Kursteil kennen Sie die elementaren Gadgets und wissen, wie sich diese erzeugen, setzen und abfragen lassen: Nat�rlich soll hier nicht nur theoretisches Wissen vermittelt werden. Wir werden es auch gleich am Beispielprogramm des ersten Kursteils �exerzieren�.

# von Michael Christoph

Die Programmierung mit Gadtools- oder 1.3-Gadget-Strukturen liegt weit in der Vergangenheit � aktuell ist die objektorientierte Programmierung, die auch bei p.OS-Gadgets zur Anwendung kommt. Wer das BOOPSI-Konzept von Kickstart 2.0 kennt oder mit der Programmierung unter MUI vertraut ist, wird auch unter p.OS keine Schwierigkeiten haben. Die Gadgets, wie auch andere Objekte, werden per pOS_NewIObject() erzeugt und mit pOS_DisposeIObject() wieder freigegeben. Auf die Freigabe der Gadgets �von Hand� k�nnen Sie aber normalerweise verzichten, da Ihnen diese Arbeit vom OS abgenommen wird. Dazu ist nur der Tag ICLTAG_AutoDelete auf TRUE zu setzen. Falls Sie mehrere Gadgets zu einer Gruppe zusammenf�gen (dazu sp�ter mehr), gen�gt es, wenn diese den Tag bekommt. Alle Members (Gruppenangeh�rige) werden dann automatisch mitgel�scht � das macht die Funktion pOS_DisposeIObject() ebenfalls.

Kurs�bersicht
Dieser dreiteilige Programmierkurs soll die Konzepte zur Entwicklung von GUIs (Graphical User Interfaces = Grafische Benutzeroberfl�chen) unter pOS vorstellen. Begleitend wird stufenweise ein einfaches Programm entwickelt, um auch die praktische Seite nicht zu vernachl�ssigen.

Folge 1: Beschreibung des pIntui-Konzepts, Beispielprogramm mit Fenster �ffnen und leerem Messageloop Am Ende dieses ersten Teils haben Sie ein Programmger�st, das die �bergebenen Argumente parsen kann, ein Fenster �ffnet und auf das Anklicken des Schlie�-Symbols wartet. Dieses Programmger�st k�nnen Sie als Ausgangsbasis f�r jedes neue Programm verwenden.

Folge 2: Beschreibung von Gadget/IObject-Konzept von pOS und BubbleHelp, Beispielprogramm erweitert um Gadgets und Messageloop. Im zweiten Kursteil wird das Fenster lebendig, d.h. wir werden es mit vielen verschiedenen Gadgettypen f�llen. Dazu wird das Gadgetkonzept von pOS n�her beleuchtet, die einzelnen Gadget-Tags vorgestellt und der Messageloop um die Gadgetinterkommunikation erweitert.

Folge 3: Beschreibung von Men�aufbau/auswertung und Requestern Beispielprogramm erweitert um Men�s, EasyRequester und Messageloop Im dritten und letzten Kursteil erh�lt das Fenster noch ein Men� spendiert und nat�rlich wird der Messageloop nochmals um die Men�verwaltung erweitert. Zus�tzlich wird ein Easy-Requester f�r die Programminformation erg�nzt.
Doch zur�ck zum Erzeugen der Gadgets. Wenn Sie ein neues Objekt generieren, m�ssen Sie den Namen des gew�nschten Objekts �bergeben. Dieser sollte jedoch nicht direkt angegeben, sondern aus der pOS_DrawInfo-Struktur des Screens gelesen werden (z.B. screen->scr_DrawInfo->dri_Names (SCRNAM_GButtonClass) f�r einen normalen Button). Beachten Sie, da� alle SCRNAM_Gxxx f�r Gadgets stehen und SCRNAM_Ixxx f�r IntuiObjekte. Bei diesen Classes (z.B. IGfxClass) handelt es sich nur um grafische Elemente, die etwas anzeigen k�nnen. Ein Button setzt sich also aus einem Gadget-Objekt f�r die Verarbeitung und einem IntuiText-Objekt f�r die Visualisierung (Beschriftung) zusammen. Durch Austausch des IntuiObjekts l��t sich dem Gadget ein ganz anderes Aussehen verleihen. So k�nnen Sie �ber das IGfx-Objekt, das Grafiken und Animationen anzeigen kann, Image-Gadgets erzeugen. Das Verhalten des Gadgets bleibt davon v�llig unber�hrt.

Der Screenshot zeigt die Gadgetgruppierung des Intui-Demoprogramms (zu finden auf der CD-ROM 9-10/97) Der Screenshot zeigt die Gadgetgruppierung des Intui-Demoprogramms (zu finden auf der CD-ROM 9-10/97)

Alle Gadgets brauchen zwingend die DrawInfo-Daten des Screens (per ICLTAG_DrawInfo). Abgeleitete Gadgets ben�tigen teilweise unbedingt noch weitere Tags. Die m�glichen Tags entnehmen Sie der Datei "p:pIntui/Tags.h". Dabei ist zu beachten, da� die abgeleiteten Gadgets alle Tags ihres Basis-Gadgets verstehen.

Wenn Sie alle Gadgets erzeugt haben, die Sie f�r die Oberfl�che ben�tigen, m�ssen die Gadgets noch plaziert werden. Unter AmigaOS war es sehr aufwendig, die Gadgets fontsensitiv zu programmieren. Je nach Fontgr��e �ndert sich die der Gadgets. Aber auch hier nimmt Ihnen p.OS die komplette Gr��enberechnung und Plazierung ab. Sie k�nnen Ihre Gadgets dadurch zwar nicht mehr auf eine feste Koordinate setzen, sondern �beschreiben� nur noch die Seite, die Vorteile der automatischen Fontsensivit�t �berwiegen aber.

Zur Anordnung der Gadgets existieren horizontale und vertikale Gruppen. Eine �Speichern/Benutzen/Abbrechen�-Gadgetgruppe wird durch eine horizontale Gruppe zusammengefa�t und nebeneinander angezeigt. Durch die Kombination mehrerer vertikaler und horizontaler Gruppen (auch gemischt mit einfachen Gadgets) k�nnen Sie das Aussehen des Fensterinneren komplett bestimmen. Soll nur einfacher Text gezeigt werden, l��t sich dies direkt mit pOS_DrawText() ausgeben. Dabei m�ssen Sie aber noch die korrekte Position berechnen und evtl. ist auch nicht genug Platz f�r den kompletten String. Da sich die Gadgets, je nach Fenstergr��e auch ausdehnen k�nnen, ist diese Positionsberechnung sehr aufwendig. Es empfiehlt sich daher, da� Sie auch dazu auf Gadgets zur�ckgreifen (SCRNAM_ GTxtBoxClass). Sie lassen sich wahlweise mit Rahmen anzeigen. Auch Textfarbe und Hintergrundverhalten k�nnen Sie festlegen. Dabei ergibt sich noch ein weiterer Vorteil: L��t sich der Text durch einen gro�en Font nicht mehr vollst�ndig sichtbar zeigen, wird der Ausgabebereich automatisch geclippt. D.h. alle p.OS-Systemgadgets zeigen sich nur so gro�, wie Platz vorhanden ist.

F�r das Abtrennen von Gadgets(gruppen) kann man statt pOS_SetPosition()/pOS_DrawLine() auf ein Trennlinien-Gadget zur�ckgreifen; z.B.

pOS_NewIObject(NULL, dri->dri_Names[SCRNAM_GBarClass], 0,
		     ICLTAG_DrawInfo, dri,  TAG_DONE);

Die Trennlinie richtet sich, je nach der Gruppe, automatisch horizontal oder vertikal aus. Wollen Sie die Zusammengeh�rigkeit mehrerer Gadgets einer Gruppe hervorheben, gen�gt es, wenn Sie in der Gruppe den Tag ICLTAG_GadBorderType (z.B. mit GADBORTYP_XBox) angeben. Die verschiedenen m�glichen Rahmentypen finden Sie in der Datei �p:pGadget/ Gadget.h�.

Werden Fenster vergr��ert oder verkleinert, pa�t p.OS automatisch (ohne jede zus�tzliche Programmzeile!) alle Gadgets der neuen Fenstergr��e an. Damit Sie nun bestimmen k�nnen, wieviel Platz ein Gadget haben darf, gibt es �Gewichte�. Ohne Gewicht (ICLTAG_Gwk = 0) wird das Gadget immer in seiner Nominalgr��e gezeichnet. Gewichte gr��er Null m�ssen immer als Ganzes in der Gruppe betrachtet werden. Nehmen wir wieder die "Speichern/Benutzen/Abbrechen"-Gruppe:

Speichern => ICLTAG_Gwk 1
Benutzen  => ICLTAG_Gwk 2
Abbrechen => ICLTAG_Gwk 3

Die Gruppe hat somit eine Gesamtgewichtanzahl von �6�. Ist das Fenster nun 600 Pixel breit, wird
Speichern => ICLTAG_Gwk 1 => 100 Pixel
Benutzen  => ICLTAG_Gwk 2 => 200 Pixel
Abbrechen => ICLTAG_Gwk 3 => 300 Pixel

breit gezeichnet, was insgesamt zur Fensterbreite von 600 Pixel f�hrt. In diesem Rechenbeispiel wird die �Luft� zwischen den Gadgets nicht ber�cksichtigt. Auch dieser Abstand l��t sich vorgeben: GRPGADTAG_SpaceVert oder GRPGADTAG_SpaceHorz, je nach Gruppe. Auch die Mindestgr��e ist zu definieren; bei Button-Gadgets z.B. mit BUTGADTAG_MinStdWidth und BUTGADTAG_MinStdHeight. Dabei beziehen sich die Zahlenwerte auf den verwendeten Font und entsprechen etwa einem Zeichen.

Soll ein Button nur etwas gr��er als normal sein, gibt es dazu die Tags ICLTAG_AddWidth und ICLTAG_AddHeight. Die Angaben erfolgen hier als Pixelanzahl. F�r die Verbreitung von Buttons k�nnen Sie auch zus�tzliche Leerzeichen im Schaltertext angeben. Dadurch pa�t sich die Gadgetgr��e dynamisch dem Font an. Beachten Sie aber, da� bei Proportionalfonts das Leerzeichen sehr schmal sein kann und dadurch nicht immer das gew�nschte Ergebnis erreicht wird. In unserem Beispielprogramm wollen wir die verwendeten Gadgettypen kurz vorstellen:

Gadget-Typen in p.OS
Unsichtbares Gadget:
dri->dri_Names[SCRNAM_GClass]
Checkbox Gadget:
dri->dri_Names[SCRNAM_GCheckBoxClass]
ICLTAG_StdLableName,    Gadgetbeschriftung
ICLTAG_Selected,        TRUE oder FALSE f�r eingedr�ckt oder nicht gedr�ckt
Button Gadget:
dri->dri_Names[SCRNAM_GButtonClass]
IOBJTAG_StaticString,   Schaltertext
String Gadget:
dri->dri_Names[SCRNAM_GStrClass]
ICLTAG_StdLableName,    Gadgetbeschriftung
STRGADTAG_MaxChars,     Anzahl m�glicher Zeichen ohne Nullbyte
STRGADTAG_String,       Vorgabetext
Listview Gadget:
dri->dri_Names[SCRNAM_GLVClass]
ICLTAG_StdLableName,    Gadgetbeschriftung
LVGADTAG_GadgetTagItem, struct pOS_GadgetTagItem-Liste mit den Eintr�gen,
			abgeschlossen mit einem NULL-Namen
LVGADTAG_ItemID,        ID des aktiven Eintrags
Popup Gadget:
dri->dri_Names[SCRNAM_GLVPopupClass]
ICLTAG_StdLableName,    Gadgetbeschriftung
LVGADTAG_GadgetTagItem, struct pOS_GadgetTagItem-Liste mit den Eintr�gen,
			abgeschlossen mit einem NULL-Namen -
LVGADTAG_ItemID,        ID des aktiven Eintrags
Slider Gadget:
dri->dri_Names[SCRNAM_GPropClass]
ICLTAG_StdLableName,    Gadgetbeschriftung
PRGADTAG_Flags,         PRGADF_FreeHoriz/PRGADF_FreeVert f�r die Verschiebbarkeit
PRGADTAG_Total,         Anzahl aller Eintr�ge
PRGADTAG_Visible,       Anzahl der sichtbaren Eintr�ge
PRGADTAG_Top,           aktueller Eintrag (Schieberposition)
Textdisplay Gadget:
dri->dri_Names[SCRNAM_GTxtBoxClass]
ICLTAG_GadBorderType,   gew�nschte Rahmenart (GADBORTYP_None f�r keinen)
IOBJTAG_StaticString,   anzuzeigender Text
IOBJTAG_BGPen,          Hintergrundfarbe
Mutualexclude Gadget:
dri->dri_Names[SCRNAM_GMXClass]
MXGADTAG_StrLabels,     CHAR ** - Liste mit den Punkten, abgeschlossen mit NULL
MXGADTAG_Active,        Index-Nummer f�r den aktiven Eintrag
Gruppen Gadget:
dri->dri_Names[SCRNAM_GGroupClass]
GRPGADTAG_BorLeft/Top/Right/Bottom, Abstand des Gadgets zum Rahmen
GRPGADTAG_Vert,                     TRUE f�r eine vertikale Gruppe (ist Default)
GRPGADTAG_Horiz,                    TRUE f�r eine horizontale Gruppe -
GRPGADTAG_TstAddGadget,             Gadget-Adresse zum Hinzuf�gen, ist der
				    Zeiger NULL, wird die komplette Gruppe
				    nicht erzeugt und alle Members freigegeben
GRPGADTAG_AddGadget,                Gadget-Adresse zum Hinzuf�gen
GRPGADTAG_RemGadget,                Gadget-Adresse zum Entfernen
GRPGADTAG_AdjustLables,             TRUE damit alle Labels gleich ausgerichtet
				    werden (ist nicht mit allen Gadgets m�glich)

Weitere m�gliche Tags und ihre Beschreibung entdeckt man in der Datei �p:pInclude/Tags.h�. Hier finden Sie auch die Ableitungen der Gadgets wieder. Beachten Sie, da� einige Gadgets auch die Tags anderer Gadgets verstehen. So k�nnen Sie f�r ein Popup-Gadget die Basis-(LVGADTAG_xxx-)Tags des ListView-Gadgets verwenden. Dadurch kann ein Listview bei Bedarf sehr einfach in ein Popup umgewandelt werden.

Andere Gadgets verstehen durch �Ableitung� die Tags ihrer Basis-Klasse. So verwendet das Textdisplay-Gadget die Tags des Button-Gadget (BUTGADTAG_xxx), das sich wiederum von gadget.class ableitet und die ICLTAG_xxx-Tags versteht. Im Beispielquellcode sehen Sie die korrekte Erzeugung all dieser Gadgettypen, teilweise mit n�tzlichen Informationen.

p.OS-Hilfe-Funktion

p.OS verf�gt �ber eine m�chtige Sprechblasen-Hilfe, die man sehr einfach produziert. Den gew�nschten Gadgets �bergeben Sie mit ICLTAG_HelpID einen String (z.B. �ButtonGad�). Eine M�glichkeit ist, den Namen des Gadgets zu verwenden. Dem Fenster teilen Sie beim �ffnen ebenfalls einen ID-String zu (SCRTAG_HelpID, z.B. den Fensternamen), damit man mehrere Gadgets in unterschiedlichen Fenstern klassifizieren kann. Zus�tzlich bekommt das Fenster noch den Namen der Hilfetexte-Datei (SCRTAG_HelpFile). Diese Datei wird, je nach eingestellter Sprache, im Verzeichnis �Help/xxx/� gesucht. Dabei wird zuerst im Programmverzeichnis nach der Datei gesucht, bevor im Systemverzeichnis nachgeforscht wird (gilt auch f�r die Suchfolge der Catalog-Dateien). Bei Bedarf k�nnen Sie auch die Gruppen-Gadgets mit einer ID ausstatten.

Wenn der Anwender mit der Maus �ber ein Gadget f�hrt, stellt p.OS nach einer (einstellbaren) Wartezeit einen Suchpfad f�r den Hilfetext zusammen. Dieser generiert sich aus der Fenster-ID, �/� und der Gadget-ID (mehrere getrennt durch �/� m�glich). Danach kann, ebenfalls durch den Schr�gstrich getrennt, der Gadgetzustand folgen (z.B. ON, OFF, DISABLED). Wollen Sie hier keine Unterscheidung machen, k�nnen Sie in der Beschreibung den Joker �*� einsetzen. Auch Default-Texte sind so m�glich, indem z.B. �*ButtonGad� angegeben wird. F�r alle Gadgets mit der ID �ButtonGad�, auch in unterschiedlichen Fenstern, wird derselbe Text erscheinen. Ausnahmef�lle k�nnen Sie zuvor durch den ausgeschriebenen Pfad bearbeiten. Deshalb empfiehlt es sich, die Default-Texte zum Schlu� anzugeben. Der eigentliche Aufbau der Hilfetext-Datei ist recht simpel:

#Hilfe-Suchpfad
(mehrzeiliger) Hilfetext
#

Per Rautenzeichen �#� mit dem Hilfe-Suchpfad wird der Anfang des Hilfetextes markiert, durch ein alleinstehendes �#� das Ende. Dazwischen k�nnen Sie den Hilfetext plazieren, der maximal zehn Zeilen zu je maximal 77 Zeichen haben darf. Es soll sich hier lediglich um eine kurze Hilfebeschreibung zur gew�hlten Funktion handeln (z.B. Auswirkung der Einstellung, auszul�sende Aktion, ...) und nicht um einen Ersatz des Handbuchs.

F�r unser Beispiel eignet sich aber auch das Inline-Hilfetext-Verfahren. Dabei k�nnen Sie auf die beiden Help-IDs im Fenster verzichten und den Hilfetext direkt im Gadget mit angeben. Dabei wird statt einem Suchpfad direkt ein Rautenzeichen, gefolgt vom Hilfetext, angegeben. Mehrzeilige Texte sind ebenfalls m�glich, wobei �*n� als Zeilentrenner zu verwenden ist. Sie k�nnen auch interne und externe Hilfetexte kombiniert einsetzen. Bei den internen Hilfetexten wird nicht zwischen den Gadgetzust�nden unterschieden, da sich nur ein String definieren l��t. Der String kann zur Laufzeit per pOS_SetGadget-Attrs() �ber ICLTAG_HelpID neu festgelegt werden. Der String mu� aber w�hrend der kompletten Laufzeit bestehen bleiben.

Nachdem Sie jetzt alles �ber die Erzeugung der Gadgets wissen, mu� unser Beispielprogramm des ersten Kursteils noch im Message-Loop erweitert werden. Gadgets k�nnen folgende Meldungen verursachen:

Gadget-Meldungen in p.OS
IDCMP_GadgetDown f�r Gadgets bei denen ICLTAG_Immediate gesetzt wurde (Gadget wurde angeklickt/Beginn der Eingabe)
IDCMP_GadgetUp f�r Gadgets bei denen ICLTAG_RelVerify gesetzt wurde (Gadget wurde losgelassen/Eingabe beendet)
IDCMP_UpdateGadget f�r Gadgets bei denen ICLTAG_UpdateGadget gesetzt wurde (z.B. �nderung des aktiven Punkts bei Listviews oder Popups)
IDCMP_GadgetAbort wenn eine Gadgeteingabe mit der rechten Maustaste abgebrochen wurde (z.B. nach Verschiebung von Slider)

Da normalerweise nicht zwischen den einzelnen Ursachen unterschieden werden mu�, k�nnen Sie die komplette Gadgetauswertung in einem Block zusammenfassen. Bei Gadget-Messages steht in �msg->im_IAddress� die Adresse des ausl�senden Gadgets. Es ist zwar auch weiterhin m�glich, per ICLTAG_ID, beim Erzeugen eine Gadget-ID f�r jedes Gadget zu vergeben und sie so zu unterscheiden, empfehlenswerter ist aber die Unterscheidung per Gadget-Adresse.

M�chten Sie feststellen, ob das Gadget zu einer bestimmten Gruppe geh�rt, k�nnen Sie das �ber die Funktion pOS_IsGadgetMember() aus der pGadget.library erfahren. Dazu ist die Gadget-Adresse und die Gruppen-Adresse zu �bergeben. Bedenken Sie, da� bei Benutzung dieser Funktion unbedingt auch die pGadget.library ge�ffnet und am Programmende wieder geschlossen werden mu�!

Im Code-Feld der Message (pOS_IntuiMessage->im_Code) steht unter p.OS ein GUI-Code, der die Aktion genauer beschreibt. Die m�glichen Aktionen sind im �p:pDevice/IEvent.h� nachzulesen (IEGUCODE_xxx). Mit pOS_GetGadgetAttrs() k�nnen Sie dann die aktuellen Daten des ver�nderten Gadgets abfragen. Von direktem Auslesen der Daten aus der Gadget-Struktur ist dringend abzuraten, da nur die Schnittstellen �ber pOS_SetGadget-Attrs() und pOS_GetGadget-Attrs() genormt und einheitlich sind. Ein Gadget kann auch private, unsichtbare Daten enthalten, die somit nicht zug�nglich w�ren. Das Auslesen des aktuellen Gadgetzustands �ber gad->gad_Flags (z.B. GFLG_Selected oder GFLG_Disabled) ist aber m�glich und erlaubt.

Die Kommunikation mit den Gadgets erfolgt �ber die bekannten Tags. Es lassen sich jedoch nicht alle Tags f�r jede Aktion verwenden. Die Verwendbarkeit der Tags wird mit �I� (Init/Create), �S� (Set) und �G� (Get) in den Includes gekennzeichnet. Die aktuellen Daten werden im Messageloop lediglich ermittelt und dann an SetDatas() zur Aktualisierung aller Gadgets weitergegeben. Dabei mu� jedoch beachtet werden, da� die Daten f�r das ausl�sende Gadget nicht gesetzt werden d�rfen! Der Grund ist recht simpel: Fahren Sie z.B. per Maus schnell durch das ListView, ist m�glicherweise bereits ein neuer Eintrag gew�hlt (Multitasking!). Setzen Sie jetzt aber den aktuellen Eintrag wieder auf den alten Wert zur�ck, dann springt der aktuelle Eintrag. M�glichweise wird dadurch auch die letzte Ver�nderung nicht mehr erfa�t.

Damit ist der zweite Kursteil beendet. Die angesprochenen Punkte finden Sie wieder im dokumentieren Sourcecode (�IntuiDemo_2.c�). Ein Beispiel f�r die externe Hilfetextdatei (�IntuiDemo.hlp�) gibt's ebenfalls hier. Im n�chsten Kursteil werden wir unser Beispielprogramm noch um Men�s erweitern.


MagnaMedia Hauptseite

� Copyright by MagnaMedia Verlag AG, Haar bei M�nchen
Ver�ffentlichung und Vervielf�ltigung nur mit schriftlicher Genehmigung des Verlags


Kommentare, Fragen, Korrekturen und Kritik bitte an Webmaster AMIGA schicken.
Zuletzt aktualisiert am 13. Juli 1997.