13. Implementieren von Menüs: Bearbeiten, Textgröße und andere, BeispielDieses Kapitel zeigt an einem Beispiel, wie man Menüs implementiert, die mit dem Target zusammenarbeiten sollen. Das sind
Der komplette Sourcecode, inklusive der hier nicht explizit dargestellten Objekte, finden Sie bei den Beispielen unter "Objekte\Allgemeines\Edit Menü & mehr". Die Grundidee ist folgende:
Die TextobjekteDie ersten beiden Texte sollen mit den Menüs interagieren und haben deswegen sowohl einen OnTargetChanged als auch einen OnSelectionChanged Handler. Beachten Sie, dass die Handler für beide Textobjekte die gleichen sind. FontSize und FontID sind jedoch unterschiedlich. Der dritte Text verfügt über keinerlei Handler. |
Memo Text1 fontSize = 14 fontID = FID_MONO text$ = "Ein Mops kam in die Küche" defaultFocus OnTargetChanged = HandleTarget OnSelectionChanged = HandleSelection fixedSize = 200, 150 End Object Memo Text2 fontSize = 24 fontID = FID_SANS text$ = "und stahl dem Koch ein Ei." OnTargetChanged = HandleTarget OnSelectionChanged = HandleSelection fixedSize = 200, 150 End Object Memo Text3 text$ = "Ich interagiere nicht mit den Menüs!" ExpandWidth End Object |
Die MenüsDas Edit-Menü ("Bearbeiten") enthält die Buttons für die entsprechenden Funktionen. Jeder Button hat seinen eigenen Actionhandler und einen Keyboard Shortcut gesetzt. Keyboard Shortcuts sind im Kapitel 3.1.4 des Objekthandbuchs beschrieben. |
Menu EditMenu
Caption$ = "Bearbeiten", 0
Children = CutButton, CopyButton, PasteButton, DeleteButton
End OBJECT
Button CutButton
Caption$ = "Ausschneiden", 0
ActionHandler = DoCut ' ButtonAction
kbdShortcut = KSM_CTRL+ KSM_PHYSICAL+ ASC("x")
End OBJECT
Button CopyButton
Caption$ = "Kopieren", 0
ActionHandler = DoCopy ' ButtonAction
kbdShortcut = KSM_CTRL+ KSM_PHYSICAL+ ASC("c")
End OBJECT
Button PasteButton
Caption$ = "Einfügen", 0
ActionHandler = DoPaste ' ButtonAction
kbdShortcut = KSM_CTRL+ KSM_PHYSICAL+ ASC("v")
End OBJECT
Button DeleteButton
Caption$ = "Löschen", 0
ActionHandler = DoDelete ' ButtonAction
kbdShortcut = &hF9A ' Siehe KeyCodes Library
End OBJECT
|
|
|
Menu FontMenu Caption$ = "Font", 0 Children = FontSelector End OBJECT RadioButtonGroup FontSelector Children = FontOption1, FontOption2, FontOption3 ApplyHandler = ChangeFont ' ListAction selection = FID_MONO End OBJECT RadioButton FontOption1 Caption$ = "Mono" : identifier = FID_MONO End OBJECT RadioButton FontOption2 Caption$ = "Sans" : identifier = FID_SANS End OBJECT RadioButton FontOption3 Caption$ = "Symbol" : identifier = FID_SYMBOLPS End OBJECT |
|
|
Menu SizeMenu Caption$ = "Größe", 0 Children = SizeSelector End OBJECT RadioButtonGroup SizeSelector Children = SizeOption1, SizeOption2, SizeOption3 orientChildren = ORIENT_VERTICALLY ' ORIENT_HORIZONTALLY ApplyHandler = ChangeSize ' ListAction selection = 14 End OBJECT RadioButton SizeOption1 Caption$ = "14 pt" : identifier = 14 End OBJECT RadioButton SizeOption2 Caption$ = "18 pt" : identifier = 18 End OBJECT RadioButton SizeOption3 Caption$ = "24 pt" : identifier = 24 End OBJECT |
Das Application ObjektDas Application Objekt muss einen OnClpChange Handler haben, damit das Clipboard überwacht werden kann. |
Application DemoApplication
Children = DemoPrimary
OnClpChange = MonitorClipboard
END Object
|
Überwachen des ClipboardDer OnClpChange Handler wird jedes Mal gerufen, wenn irgendeine Applikation Änderungen am Clipboard vornimmt. Das schließt unser eigenes Programm mit ein. Wir müssen daher nur nachschauen, ob ein Text im Clipboard ist und den "Einfügen" Button enablen oder disablen. Dazu verwenden wir die BASIC Routine ClipboardTest. Das Format der Daten, die sich im Clipboard befinden, wird durch eine eindeutige Kombination aus ManufacturerID (manufID) und Format-Nummer (formatNo) gekennzeichnet. Für Texte gilt: manufID = 0 (GeoWorks), formatNo. = 0 (TEXT). Mehr dazu finden Sie im Kapitel 5 "Arbeit mit der Zwischenablage". |
SYSTEMACTION MonitorClipboard
IF ClipboardTest(0, 0) then
PasteButton.enabled = TRUE
else
PasteButton.enabled = FALSE
End IF
END ACTION ' MonitorClipboard
|
Sollte gerade ein Objekt das Target sein, dass nicht mit den Menüs zusammenarbeiten soll oder kann, so stört uns das hier nicht, da wir an anderer Stelle sicherstellen, dass das Menu-Objekt dann nicht enabled ist. Der PasteButton ist dann niemals aktiv, auch wenn er enabled ist.
Die anderen Buttons des Bearbeiten-MenüsAlle anderen Buttons des Bearbeiten-Menüs müssen enabled werden wenn der Nutzer Text selektiert hat, andernfalls müssen sie disabled werden. Da diese Abfrage mehrfach gebraucht wird lagern wir sie in eine SUB aus. Der Parameter textObj bezeichnet das aktuelle Targetobjekt, die Instancevariable selectionLen enthält die Anzahl der selektierten Zeichen. |
SUB UpdateEditMenu (textObj as OBJECT)
IF textObj.selectionLen THEN ' d.h. selectionLen <> 0
CopyButton.enabled = TRUE
CutButton.enabled = TRUE
DeleteButton.enabled = TRUE
ELSE
CopyButton.enabled = FALSE
CutButton.enabled = FALSE
DeleteButton.enabled = FALSE
END IF
END SUB 'UpdateEditMenu
|
Die Textobjekt- HandlerJetzt kümmern wir uns darum, was passiert, wenn der Nutzer ein Textobjekt anklickt. Das entsprechende Objekt wird dann zum Target. Vorher - und das ist sehr wichtig für uns - verliert das Objekt, das bis dahin Target war, jedoch seine Target-Status. Der OnTargetChanged Handler wird also zweimal gerufen: zuerst von dem Objekt das bis dahin Target war (mit dem Parameter hasTarget = FALSE) und danach von dem Objekt das jetzt Target wird (mit dem Parameter hasTarget = TRUE).Wird er Handler also wegen einem Targetverlust gerufen disablen wir einfach alle Menüs, andernfalls enablen wir sie und updaten die UI. Das hat einen weiteren Vorteil: Klickt der User auf ein Objekt, dass nicht mit den Menüs zusammenarbeiten kann oder soll (in unserem Fall Text3), so wird der OnTargetChanged Handler nur einmal gerufen (mit hasTarget = FALSE) und die Menüs bleiben so lange disabled, bis der Nutzer wieder in eins der Objekte Text1 oder Text2 klickt. |
TARGETACTION HandleTarget
if hasTarget = FALSE THEN
Editmenu.enabled = FALSE
Fontmenu.enabled = FALSE
Sizemenu.enabled = FALSE
return
end if
EditMenu.enabled = TRUE
UpdateEditMenu (sender)
FontMenu.enabled = TRUE
FontSelector.selection = sender.fontID
SizeMenu.enabled = TRUE
SizeSelector.selection = sender.fontSize
END ACTION ' HandleTarget
|
|
|
TEXTACTION HandleSelection UpdateEditMenu (sender) END ACTION |
Handler des Bearbeiten-MenüsNun müssen wir die ActionHandler der Buttons aus dem Bearbeiten-Menü implementieren. Da wir nicht wissen können, ob das aktuelle Target das Objekt Text1 oder Text2 ist verwenden wir als Ziel die globale Variable Target. Wenn die Handler der Menüs aufgerufen werden kann dies nur eines der beiden genannten Objekte sein, da wir vorne sichergestellt haben das die Menüs nur aktiv sind, wenn eines dieser Objekte das Target ist.Die Handler an sich sind relativ einfach. Wir verwenden die für alle GenericClass Objekte definierten Clipboard-Methoden ClpCopy und ClpPaste. Ausschneiden entspricht einem Kopieren in das Clipboard mit anschließendem Löschen. Für das Löschen verwenden wir die Textobjekt Methode DeleteSelection. Außerdem müssen wir noch das Edit-Menü updaten wenn wir etwas gelöscht haben. Dazu greifen wir wieder auf die globale Variable Target zurück |
BUTTONACTION DoCut Target.ClpCopy Target.DeleteSelection UpdateEditMenu (Target) END ACTION BUTTONACTION DoCopy Target.ClpCopy END ACTION BUTTONACTION DoPaste Target.ClpPaste END ACTION BUTTONACTION DoDelete Target.DeleteSelection UpdateEditMenu (Target) END ACTION |
Die anderen Menü-HandlerDie Menüs - und damit die RadioButtonGroups - sind nur aktiv, wenn eines der Objekte Text1 oder Text2 das Target ist. Da die Identifier der RadioButton-Objekte ihre Funktion (eine FontID oder eine Schriftgröße) widerspiegeln werden die Actionhandler der RadioButtonGroups sehr einfach. |
LISTACTION ChangeFont Target.fontID = selection END ACTION' ChangeFont LISTACTION ChangeSize Target.fontsize = selection END ACTION' ChangeSize |
Abschließende ÜberlegungenBesondere Aufmerksamkeit verdient die Frage, ob die Menüs am Programmstart wirklich immer die korrekte Situation widerspiegeln. In unserem Fall müssen die Menüs die Eigenschaften des Objekts Text1 widerspiegeln, weil dieses Objekt den Hint defaultFocus gesetzt hat. Hier ist manchmal etwas Handarbeit (setzen der richtigen Startwerte) angesagt.Der eleganteste und sicherste Weg um eventuell verbleibende Probleme zu umgehen ist, einen OnStartup Handler für das Applicationobjekt zu schreiben. Dort können Sie die UI-Objekte Ihren Vorstellungen nach anpassen. |
Application DemoApplication Children = DemoPrimary OnStartup = StartupCode ... END Object |
SYSTEMACTION StartupCode UpdateEditMenu (Text1) ... END ACTION ' StartupCode |
|
Für den Anfänger ist es oft sehr schwer, die Zusammenhänge zu überblicken. In unserem Fall stellt sich die Situation für einen erfahrenen Programmierer so dar: Für den Zustand der Menüs sind genau zwei Routinen zuständig: der Actionhandler MonitorClipboard und die SUB UpdateEditMenu. MonitorClipboard wird am Programmstart automatisch gerufen. Da das Objekt Text1 den Hint defaultFocus gesetzt hat, wird es am Programmstart auch gleichzeitig zum Target. Damit wird der Handler HandleTarget mit dem Parameter hasTarget = TRUE am Programmstart gerufen, was den Aufruf von UpdateEditMenu zur Folge hat. Damit sind die Menüs auf dem aktuellen Stand. Wie gesagt, der Einsteiger überblickt so etwas nicht.
Deswegen die folgenden Tipps:
^ |